authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2023-08-31 07:50:29+02:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2023-08-31 07:50:29+02:00
logf4c9e19bc3213c2bc7e03d7b06d7129882f39f6c
tree79d343002bb63a44f8ab0dbac0c9f4ec54078c3a
parente2ff486de5f3aceb21730e1feabbaf9b03432660
parent19a1332ca140274d03e57d31fda7748a8a3641ba
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #17020 from ziglang/macho-versions

macho: big-ish refactor and report errors to the user using Zig's API

29 files changed, 5893 insertions(+), 7017 deletions(-)

CMakeLists.txt-1
......@@ -588,7 +588,6 @@ set(ZIG_STAGE2_SOURCES
588588 "${CMAKE_SOURCE_DIR}/src/link/MachO/Relocation.zig"
589589 "${CMAKE_SOURCE_DIR}/src/link/MachO/Trie.zig"
590590 "${CMAKE_SOURCE_DIR}/src/link/MachO/UnwindInfo.zig"
591 "${CMAKE_SOURCE_DIR}/src/link/MachO/ZldAtom.zig"
592591 "${CMAKE_SOURCE_DIR}/src/link/MachO/dyld_info/bind.zig"
593592 "${CMAKE_SOURCE_DIR}/src/link/MachO/dyld_info/Rebase.zig"
594593 "${CMAKE_SOURCE_DIR}/src/link/MachO/dead_strip.zig"
src/Compilation.zig+16
......@@ -2609,6 +2609,9 @@ pub fn totalErrorCount(self: *Compilation) u32 {
26092609 }
26102610 total += @intFromBool(self.link_error_flags.missing_libc);
26112611
2612 // Misc linker errors
2613 total += self.bin_file.miscErrors().len;
2614
26122615 // Compile log errors only count if there are no other errors.
26132616 if (total == 0) {
26142617 if (self.bin_file.options.module) |module| {
......@@ -2759,6 +2762,19 @@ pub fn getAllErrorsAlloc(self: *Compilation) !ErrorBundle {
27592762 }));
27602763 }
27612764
2765 for (self.bin_file.miscErrors()) |link_err| {
2766 try bundle.addRootErrorMessage(.{
2767 .msg = try bundle.addString(link_err.msg),
2768 .notes_len = @intCast(link_err.notes.len),
2769 });
2770 const notes_start = try bundle.reserveNotes(@intCast(link_err.notes.len));
2771 for (link_err.notes, 0..) |note, i| {
2772 bundle.extra.items[notes_start + i] = @intFromEnum(try bundle.addErrorMessage(.{
2773 .msg = try bundle.addString(note.msg),
2774 }));
2775 }
2776 }
2777
27622778 if (self.bin_file.options.module) |module| {
27632779 if (bundle.root_list.items.len == 0 and module.compile_log_decls.count() != 0) {
27642780 const keys = module.compile_log_decls.keys();
src/arch/aarch64/Emit.zig+4-4
......@@ -670,7 +670,7 @@ fn mirCallExtern(emit: *Emit, inst: Mir.Inst.Index) !void {
670670
671671 if (emit.bin_file.cast(link.File.MachO)) |macho_file| {
672672 // Add relocation to the decl.
673 const atom_index = macho_file.getAtomIndexForSymbol(.{ .sym_index = relocation.atom_index, .file = null }).?;
673 const atom_index = macho_file.getAtomIndexForSymbol(.{ .sym_index = relocation.atom_index }).?;
674674 const target = macho_file.getGlobalByIndex(relocation.sym_index);
675675 try link.File.MachO.Atom.addRelocation(macho_file, atom_index, .{
676676 .type = .branch,
......@@ -885,9 +885,9 @@ fn mirLoadMemoryPie(emit: *Emit, inst: Mir.Inst.Index) !void {
885885 if (emit.bin_file.cast(link.File.MachO)) |macho_file| {
886886 const Atom = link.File.MachO.Atom;
887887 const Relocation = Atom.Relocation;
888 const atom_index = macho_file.getAtomIndexForSymbol(.{ .sym_index = data.atom_index, .file = null }).?;
888 const atom_index = macho_file.getAtomIndexForSymbol(.{ .sym_index = data.atom_index }).?;
889889 try Atom.addRelocations(macho_file, atom_index, &[_]Relocation{ .{
890 .target = .{ .sym_index = data.sym_index, .file = null },
890 .target = .{ .sym_index = data.sym_index },
891891 .offset = offset,
892892 .addend = 0,
893893 .pcrel = true,
......@@ -898,7 +898,7 @@ fn mirLoadMemoryPie(emit: *Emit, inst: Mir.Inst.Index) !void {
898898 else => unreachable,
899899 },
900900 }, .{
901 .target = .{ .sym_index = data.sym_index, .file = null },
901 .target = .{ .sym_index = data.sym_index },
902902 .offset = offset + 4,
903903 .addend = 0,
904904 .pcrel = false,
src/arch/x86_64/Emit.zig+3-8
......@@ -43,9 +43,7 @@ pub fn emitMir(emit: *Emit) Error!void {
4343 }),
4444 .linker_extern_fn => |symbol| if (emit.bin_file.cast(link.File.MachO)) |macho_file| {
4545 // Add relocation to the decl.
46 const atom_index = macho_file.getAtomIndexForSymbol(
47 .{ .sym_index = symbol.atom_index, .file = null },
48 ).?;
46 const atom_index = macho_file.getAtomIndexForSymbol(.{ .sym_index = symbol.atom_index }).?;
4947 const target = macho_file.getGlobalByIndex(symbol.sym_index);
5048 try link.File.MachO.Atom.addRelocation(macho_file, atom_index, .{
5149 .type = .branch,
......@@ -77,10 +75,7 @@ pub fn emitMir(emit: *Emit) Error!void {
7775 .linker_import,
7876 .linker_tlv,
7977 => |symbol| if (emit.bin_file.cast(link.File.MachO)) |macho_file| {
80 const atom_index = macho_file.getAtomIndexForSymbol(.{
81 .sym_index = symbol.atom_index,
82 .file = null,
83 }).?;
78 const atom_index = macho_file.getAtomIndexForSymbol(.{ .sym_index = symbol.atom_index }).?;
8479 try link.File.MachO.Atom.addRelocation(macho_file, atom_index, .{
8580 .type = switch (lowered_relocs[0].target) {
8681 .linker_got => .got,
......@@ -88,7 +83,7 @@ pub fn emitMir(emit: *Emit) Error!void {
8883 .linker_tlv => .tlv,
8984 else => unreachable,
9085 },
91 .target = .{ .sym_index = symbol.sym_index, .file = null },
86 .target = .{ .sym_index = symbol.sym_index },
9287 .offset = @as(u32, @intCast(end_offset - 4)),
9388 .addend = 0,
9489 .pcrel = true,
src/link.zig+20-19
......@@ -228,7 +228,6 @@ pub const Options = struct {
228228
229229 version: ?std.SemanticVersion,
230230 compatibility_version: ?std.SemanticVersion,
231 darwin_sdk_version: ?std.SemanticVersion = null,
232231 libc_installation: ?*const LibCInstallation,
233232
234233 dwarf_format: ?std.dwarf.Format,
......@@ -694,19 +693,15 @@ pub const File = struct {
694693 /// TODO audit this error set. most of these should be collapsed into one error,
695694 /// and ErrorFlags should be updated to convey the meaning to the user.
696695 pub const FlushError = error{
697 BadDwarfCfi,
698696 CacheUnavailable,
699697 CurrentWorkingDirectoryUnlinked,
700698 DivisionByZero,
701699 DllImportLibraryNotFound,
702 EmptyStubFile,
703700 ExpectedFuncType,
704701 FailedToEmit,
705 FailedToResolveRelocationTarget,
706702 FileSystem,
707703 FilesOpenedWithWrongFlags,
708704 FlushFailure,
709 FrameworkNotFound,
710705 FunctionSignatureMismatch,
711706 GlobalTypeMismatch,
712707 HotSwapUnavailableOnHostOperatingSystem,
......@@ -723,27 +718,19 @@ pub const File = struct {
723718 LLD_LinkingIsTODO_ForSpirV,
724719 LibCInstallationMissingCRTDir,
725720 LibCInstallationNotAvailable,
726 LibraryNotFound,
727721 LinkingWithoutZigSourceUnimplemented,
728722 MalformedArchive,
729723 MalformedDwarf,
730724 MalformedSection,
731725 MemoryTooBig,
732726 MemoryTooSmall,
733 MismatchedCpuArchitecture,
734727 MissAlignment,
735728 MissingEndForBody,
736729 MissingEndForExpression,
737 /// TODO: this should be removed from the error set in favor of using ErrorFlags
738 MissingMainEntrypoint,
739 /// TODO: this should be removed from the error set in favor of using ErrorFlags
740 MissingSection,
741730 MissingSymbol,
742731 MissingTableSymbols,
743732 ModuleNameMismatch,
744 MultipleSymbolDefinitions,
745733 NoObjectsToLink,
746 NotObject,
747734 NotObjectFile,
748735 NotSupported,
749736 OutOfMemory,
......@@ -755,21 +742,15 @@ pub const File = struct {
755742 SymbolMismatchingType,
756743 TODOImplementPlan9Objs,
757744 TODOImplementWritingLibFiles,
758 TODOImplementWritingStaticLibFiles,
759745 UnableToSpawnSelf,
760746 UnableToSpawnWasm,
761747 UnableToWriteArchive,
762748 UndefinedLocal,
763 /// TODO: merge with UndefinedSymbolReference
764749 UndefinedSymbol,
765 /// TODO: merge with UndefinedSymbol
766 UndefinedSymbolReference,
767750 Underflow,
768751 UnexpectedRemainder,
769752 UnexpectedTable,
770753 UnexpectedValue,
771 UnhandledDwFormValue,
772 UnhandledSymbolType,
773754 UnknownFeature,
774755 Unseekable,
775756 UnsupportedCpuArchitecture,
......@@ -866,6 +847,13 @@ pub const File = struct {
866847 }
867848 }
868849
850 pub fn miscErrors(base: *File) []const ErrorMsg {
851 switch (base.tag) {
852 .macho => return @fieldParentPtr(MachO, "base", base).misc_errors.items,
853 else => return &.{},
854 }
855 }
856
869857 pub const UpdateDeclExportsError = error{
870858 OutOfMemory,
871859 AnalysisFail,
......@@ -1129,6 +1117,19 @@ pub const File = struct {
11291117 missing_libc: bool = false,
11301118 };
11311119
1120 pub const ErrorMsg = struct {
1121 msg: []const u8,
1122 notes: []ErrorMsg = &.{},
1123
1124 pub fn deinit(self: *ErrorMsg, gpa: Allocator) void {
1125 for (self.notes) |*note| {
1126 note.deinit(gpa);
1127 }
1128 gpa.free(self.notes);
1129 gpa.free(self.msg);
1130 }
1131 };
1132
11321133 pub const LazySymbol = struct {
11331134 pub const Kind = enum { code, const_data };
11341135
src/link/MachO.zig+2250-908
......@@ -1,100 +1,3 @@
1const MachO = @This();
2
3const std = @import("std");
4const build_options = @import("build_options");
5const builtin = @import("builtin");
6const assert = std.debug.assert;
7const dwarf = std.dwarf;
8const fs = std.fs;
9const log = std.log.scoped(.link);
10const macho = std.macho;
11const math = std.math;
12const mem = std.mem;
13const meta = std.meta;
14
15const aarch64 = @import("../arch/aarch64/bits.zig");
16const calcUuid = @import("MachO/uuid.zig").calcUuid;
17const codegen = @import("../codegen.zig");
18const dead_strip = @import("MachO/dead_strip.zig");
19const fat = @import("MachO/fat.zig");
20const link = @import("../link.zig");
21const llvm_backend = @import("../codegen/llvm.zig");
22const load_commands = @import("MachO/load_commands.zig");
23const stubs = @import("MachO/stubs.zig");
24const target_util = @import("../target.zig");
25const trace = @import("../tracy.zig").trace;
26const zld = @import("MachO/zld.zig");
27
28const Air = @import("../Air.zig");
29const Allocator = mem.Allocator;
30const Archive = @import("MachO/Archive.zig");
31pub const Atom = @import("MachO/Atom.zig");
32const Cache = std.Build.Cache;
33const CodeSignature = @import("MachO/CodeSignature.zig");
34const Compilation = @import("../Compilation.zig");
35const Dwarf = File.Dwarf;
36const Dylib = @import("MachO/Dylib.zig");
37const File = link.File;
38const Object = @import("MachO/Object.zig");
39const LibStub = @import("tapi.zig").LibStub;
40const Liveness = @import("../Liveness.zig");
41const LlvmObject = @import("../codegen/llvm.zig").Object;
42const Md5 = std.crypto.hash.Md5;
43const Module = @import("../Module.zig");
44const InternPool = @import("../InternPool.zig");
45const Relocation = @import("MachO/Relocation.zig");
46const StringTable = @import("strtab.zig").StringTable;
47const TableSection = @import("table_section.zig").TableSection;
48const Trie = @import("MachO/Trie.zig");
49const Type = @import("../type.zig").Type;
50const TypedValue = @import("../TypedValue.zig");
51const Value = @import("../value.zig").Value;
52
53pub const DebugSymbols = @import("MachO/DebugSymbols.zig");
54
55const Bind = @import("MachO/dyld_info/bind.zig").Bind(*const MachO, MachO.SymbolWithLoc);
56const LazyBind = @import("MachO/dyld_info/bind.zig").LazyBind(*const MachO, MachO.SymbolWithLoc);
57const Rebase = @import("MachO/dyld_info/Rebase.zig");
58
59pub const base_tag: File.Tag = File.Tag.macho;
60
61/// Mode of operation of the linker.
62pub const Mode = enum {
63 /// Incremental mode will preallocate segments/sections and is compatible with
64 /// watch and HCS modes of operation.
65 incremental,
66 /// Zld mode will link relocatables in a traditional, one-shot
67 /// fashion (default for LLVM backend). It acts as a drop-in replacement for
68 /// LLD.
69 zld,
70};
71
72const Section = struct {
73 header: macho.section_64,
74 segment_index: u8,
75
76 // TODO is null here necessary, or can we do away with tracking via section
77 // size in incremental context?
78 last_atom_index: ?Atom.Index = null,
79
80 /// A list of atoms that have surplus capacity. This list can have false
81 /// positives, as functions grow and shrink over time, only sometimes being added
82 /// or removed from the freelist.
83 ///
84 /// An atom has surplus capacity when its overcapacity value is greater than
85 /// padToIdeal(minimum_atom_size). That is, when it has so
86 /// much extra capacity, that we could fit a small new symbol in it, itself with
87 /// ideal_capacity or more.
88 ///
89 /// Ideal capacity is defined by size + (size / ideal_factor).
90 ///
91 /// Overcapacity is measured by actual_capacity - ideal_capacity. Note that
92 /// overcapacity can be negative. A simple way to have negative overcapacity is to
93 /// allocate a fresh atom, which will have ideal capacity, and then grow it
94 /// by 1 byte. It will then have -1 overcapacity.
95 free_list: std.ArrayListUnmanaged(Atom.Index) = .{},
96};
97
981base: File,
992
1003/// If this is not null, an object file is created by LLVM and linked with LLD afterwards.
......@@ -103,18 +6,18 @@ llvm_object: ?*LlvmObject = null,
1036/// Debug symbols bundle (or dSym).
1047d_sym: ?DebugSymbols = null,
1058
106/// Page size is dependent on the target cpu architecture.
107/// For x86_64 that's 4KB, whereas for aarch64, that's 16KB.
108page_size: u16,
109
1109mode: Mode,
11110
11211dyld_info_cmd: macho.dyld_info_command = .{},
11312symtab_cmd: macho.symtab_command = .{},
11413dysymtab_cmd: macho.dysymtab_command = .{},
115uuid_cmd: macho.uuid_command = .{},
14function_starts_cmd: macho.linkedit_data_command = .{ .cmd = .FUNCTION_STARTS },
15data_in_code_cmd: macho.linkedit_data_command = .{ .cmd = .DATA_IN_CODE },
16uuid_cmd: macho.uuid_command = .{ .uuid = [_]u8{0} ** 16 },
11617codesig_cmd: macho.linkedit_data_command = .{ .cmd = .CODE_SIGNATURE },
11718
19objects: std.ArrayListUnmanaged(Object) = .{},
20archives: std.ArrayListUnmanaged(Archive) = .{},
11821dylibs: std.ArrayListUnmanaged(Dylib) = .{},
11922dylibs_map: std.StringHashMapUnmanaged(u16) = .{},
12023referenced_dylibs: std.AutoArrayHashMapUnmanaged(u16, void) = .{},
......@@ -130,14 +33,19 @@ data_segment_cmd_index: ?u8 = null,
13033linkedit_segment_cmd_index: ?u8 = null,
13134
13235text_section_index: ?u8 = null,
133stubs_section_index: ?u8 = null,
134stub_helper_section_index: ?u8 = null,
135got_section_index: ?u8 = null,
13636data_const_section_index: ?u8 = null,
137la_symbol_ptr_section_index: ?u8 = null,
13837data_section_index: ?u8 = null,
38bss_section_index: ?u8 = null,
13939thread_vars_section_index: ?u8 = null,
14040thread_data_section_index: ?u8 = null,
41thread_bss_section_index: ?u8 = null,
42eh_frame_section_index: ?u8 = null,
43unwind_info_section_index: ?u8 = null,
44stubs_section_index: ?u8 = null,
45stub_helper_section_index: ?u8 = null,
46got_section_index: ?u8 = null,
47la_symbol_ptr_section_index: ?u8 = null,
48tlv_ptr_section_index: ?u8 = null,
14149
14250locals: std.ArrayListUnmanaged(macho.nlist_64) = .{},
14351globals: std.ArrayListUnmanaged(SymbolWithLoc) = .{},
......@@ -154,8 +62,13 @@ strtab: StringTable(.strtab) = .{},
15462
15563got_table: TableSection(SymbolWithLoc) = .{},
15664stub_table: TableSection(SymbolWithLoc) = .{},
65tlv_ptr_table: TableSection(SymbolWithLoc) = .{},
66
67thunk_table: std.AutoHashMapUnmanaged(Atom.Index, thunks.Thunk.Index) = .{},
68thunks: std.ArrayListUnmanaged(thunks.Thunk) = .{},
15769
15870error_flags: File.ErrorFlags = File.ErrorFlags{},
71misc_errors: std.ArrayListUnmanaged(File.ErrorMsg) = .{},
15972
16073segment_table_dirty: bool = false,
16174got_table_count_dirty: bool = false,
......@@ -225,108 +138,6 @@ tlv_table: TlvSymbolTable = .{},
225138/// Hot-code swapping state.
226139hot_state: if (is_hot_update_compatible) HotUpdateState else struct {} = .{},
227140
228const is_hot_update_compatible = switch (builtin.target.os.tag) {
229 .macos => true,
230 else => false,
231};
232
233const LazySymbolTable = std.AutoArrayHashMapUnmanaged(Module.Decl.OptionalIndex, LazySymbolMetadata);
234
235const LazySymbolMetadata = struct {
236 const State = enum { unused, pending_flush, flushed };
237 text_atom: Atom.Index = undefined,
238 data_const_atom: Atom.Index = undefined,
239 text_state: State = .unused,
240 data_const_state: State = .unused,
241};
242
243const TlvSymbolTable = std.AutoArrayHashMapUnmanaged(SymbolWithLoc, Atom.Index);
244
245const DeclMetadata = struct {
246 atom: Atom.Index,
247 section: u8,
248 /// A list of all exports aliases of this Decl.
249 /// TODO do we actually need this at all?
250 exports: std.ArrayListUnmanaged(u32) = .{},
251
252 fn getExport(m: DeclMetadata, macho_file: *const MachO, name: []const u8) ?u32 {
253 for (m.exports.items) |exp| {
254 if (mem.eql(u8, name, macho_file.getSymbolName(.{
255 .sym_index = exp,
256 .file = null,
257 }))) return exp;
258 }
259 return null;
260 }
261
262 fn getExportPtr(m: *DeclMetadata, macho_file: *MachO, name: []const u8) ?*u32 {
263 for (m.exports.items) |*exp| {
264 if (mem.eql(u8, name, macho_file.getSymbolName(.{
265 .sym_index = exp.*,
266 .file = null,
267 }))) return exp;
268 }
269 return null;
270 }
271};
272
273const BindingTable = std.AutoArrayHashMapUnmanaged(Atom.Index, std.ArrayListUnmanaged(Atom.Binding));
274const UnnamedConstTable = std.AutoArrayHashMapUnmanaged(Module.Decl.Index, std.ArrayListUnmanaged(Atom.Index));
275const RebaseTable = std.AutoArrayHashMapUnmanaged(Atom.Index, std.ArrayListUnmanaged(u32));
276const RelocationTable = std.AutoArrayHashMapUnmanaged(Atom.Index, std.ArrayListUnmanaged(Relocation));
277
278const ResolveAction = struct {
279 kind: Kind,
280 target: SymbolWithLoc,
281
282 const Kind = enum {
283 none,
284 add_got,
285 add_stub,
286 };
287};
288
289pub const SymbolWithLoc = struct {
290 // Index into the respective symbol table.
291 sym_index: u32,
292
293 // null means it's a synthetic global.
294 file: ?u32 = null,
295
296 pub fn eql(this: SymbolWithLoc, other: SymbolWithLoc) bool {
297 if (this.file == null and other.file == null) {
298 return this.sym_index == other.sym_index;
299 }
300 if (this.file != null and other.file != null) {
301 return this.sym_index == other.sym_index and this.file.? == other.file.?;
302 }
303 return false;
304 }
305};
306
307const HotUpdateState = struct {
308 mach_task: ?std.os.darwin.MachTask = null,
309};
310
311/// When allocating, the ideal_capacity is calculated by
312/// actual_capacity + (actual_capacity / ideal_factor)
313const ideal_factor = 3;
314
315/// In order for a slice of bytes to be considered eligible to keep metadata pointing at
316/// it as a possible place to put new symbols, it must have enough room for this many bytes
317/// (plus extra for reserved capacity).
318const minimum_text_block_size = 64;
319pub const min_text_capacity = padToIdeal(minimum_text_block_size);
320
321/// Default virtual memory offset corresponds to the size of __PAGEZERO segment and
322/// start of __TEXT segment.
323pub const default_pagezero_vmsize: u64 = 0x100000000;
324
325/// We commit 0x1000 = 4096 bytes of space to the header and
326/// the table of load commands. This should be plenty for any
327/// potential future extensions.
328pub const default_headerpad_size: u32 = 0x1000;
329
330141pub fn openPath(allocator: Allocator, options: link.Options) !*MachO {
331142 assert(options.target.ofmt == .macho);
332143
......@@ -396,7 +207,6 @@ pub fn openPath(allocator: Allocator, options: link.Options) !*MachO {
396207 .allocator = allocator,
397208 .dwarf = link.File.Dwarf.init(allocator, &self.base, options.target),
398209 .file = d_sym_file,
399 .page_size = self.page_size,
400210 };
401211 }
402212
......@@ -413,16 +223,13 @@ pub fn openPath(allocator: Allocator, options: link.Options) !*MachO {
413223 try self.populateMissingMetadata();
414224
415225 if (self.d_sym) |*d_sym| {
416 try d_sym.populateMissingMetadata();
226 try d_sym.populateMissingMetadata(self);
417227 }
418228
419229 return self;
420230}
421231
422232pub fn createEmpty(gpa: Allocator, options: link.Options) !*MachO {
423 const cpu_arch = options.target.cpu.arch;
424 const page_size: u16 = if (cpu_arch == .aarch64) 0x4000 else 0x1000;
425
426233 const self = try gpa.create(MachO);
427234 errdefer gpa.destroy(self);
428235
......@@ -433,7 +240,6 @@ pub fn createEmpty(gpa: Allocator, options: link.Options) !*MachO {
433240 .allocator = gpa,
434241 .file = null,
435242 },
436 .page_size = page_size,
437243 .mode = if (options.use_llvm or options.module == null or options.cache_mode == .whole)
438244 .zld
439245 else
......@@ -461,8 +267,11 @@ pub fn flush(self: *MachO, comp: *Compilation, prog_node: *std.Progress.Node) li
461267 if (build_options.have_llvm) {
462268 return self.base.linkAsArchive(comp, prog_node);
463269 } else {
464 log.err("TODO: non-LLVM archiver for MachO object files", .{});
465 return error.TODOImplementWritingStaticLibFiles;
270 try self.misc_errors.ensureUnusedCapacity(self.base.allocator, 1);
271 self.misc_errors.appendAssumeCapacity(.{
272 .msg = try self.base.allocator.dupe(u8, "TODO: non-LLVM archiver for MachO object files"),
273 });
274 return error.FlushFailure;
466275 }
467276 }
468277
......@@ -590,36 +399,40 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No
590399 self.dylibs_map.clearRetainingCapacity();
591400 self.referenced_dylibs.clearRetainingCapacity();
592401
593 var dependent_libs = std.fifo.LinearFifo(struct {
594 id: Dylib.Id,
595 parent: u16,
596 }, .Dynamic).init(arena);
402 var dependent_libs = std.fifo.LinearFifo(DylibReExportInfo, .Dynamic).init(arena);
597403
598 try self.parseLibs(libs.keys(), libs.values(), self.base.options.sysroot, &dependent_libs);
599 try self.parseDependentLibs(self.base.options.sysroot, &dependent_libs);
600 }
404 for (libs.keys(), libs.values()) |path, lib| {
405 const in_file = try std.fs.cwd().openFile(path, .{});
406 defer in_file.close();
601407
602 if (self.dyld_stub_binder_index == null) {
603 self.dyld_stub_binder_index = try self.addUndefined("dyld_stub_binder", .add_got);
604 }
605 if (!self.base.options.single_threaded) {
606 _ = try self.addUndefined("__tlv_bootstrap", .none);
607 }
408 var parse_ctx = ParseErrorCtx.init(self.base.allocator);
409 defer parse_ctx.deinit();
608410
609 try self.createMhExecuteHeaderSymbol();
411 self.parseLibrary(
412 in_file,
413 path,
414 lib,
415 false,
416 false,
417 null,
418 &dependent_libs,
419 &parse_ctx,
420 ) catch |err| try self.handleAndReportParseError(path, err, &parse_ctx);
421 }
422
423 try self.parseDependentLibs(&dependent_libs);
424 }
610425
611426 var actions = std.ArrayList(ResolveAction).init(self.base.allocator);
612427 defer actions.deinit();
613 try self.resolveSymbolsInDylibs(&actions);
428 try self.resolveSymbols(&actions);
614429
430 if (self.getEntryPoint() == null) {
431 self.error_flags.no_entry_point_found = true;
432 }
615433 if (self.unresolved.count() > 0) {
616 for (self.unresolved.keys()) |index| {
617 // TODO: convert into compiler errors.
618 const global = self.globals.items[index];
619 const sym_name = self.getSymbolName(global);
620 log.err("undefined symbol reference '{s}'", .{sym_name});
621 }
622 return error.UndefinedSymbolReference;
434 try self.reportUndefined();
435 return error.FlushFailure;
623436 }
624437
625438 for (actions.items) |action| switch (action.kind) {
......@@ -631,6 +444,16 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No
631444 try self.createDyldPrivateAtom();
632445 try self.writeStubHelperPreamble();
633446
447 if (self.base.options.output_mode == .Exe and self.getEntryPoint() != null) {
448 const global = self.getEntryPoint().?;
449 if (self.getSymbol(global).undf()) {
450 // We do one additional check here in case the entry point was found in one of the dylibs.
451 // (I actually have no idea what this would imply but it is a possible outcome and so we
452 // support it.)
453 try self.addStubEntry(global);
454 }
455 }
456
634457 try self.allocateSpecialSymbols();
635458
636459 for (self.relocs.keys()) |atom_index| {
......@@ -685,20 +508,13 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No
685508
686509 try self.writeLinkeditSegmentData();
687510
688 const target = self.base.options.target;
689 const requires_codesig = blk: {
690 if (self.base.options.entitlements) |_| break :blk true;
691 if (target.cpu.arch == .aarch64 and (target.os.tag == .macos or target.abi == .simulator))
692 break :blk true;
693 break :blk false;
694 };
695 var codesig: ?CodeSignature = if (requires_codesig) blk: {
511 var codesig: ?CodeSignature = if (requiresCodeSignature(&self.base.options)) blk: {
696512 // Preallocate space for the code signature.
697513 // We need to do this at this stage so that we have the load commands with proper values
698514 // written out to the file.
699515 // The most important here is to have the correct vm and filesize of the __LINKEDIT segment
700516 // where the code signature goes into.
701 var codesig = CodeSignature.init(self.page_size);
517 var codesig = CodeSignature.init(getPageSize(self.base.options.target.cpu.arch));
702518 codesig.code_directory.ident = self.base.options.emit.?.sub_path;
703519 if (self.base.options.entitlements) |path| {
704520 try codesig.addEntitlements(self.base.allocator, path);
......@@ -722,16 +538,18 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No
722538 .Exe => blk: {
723539 const seg_id = self.header_segment_cmd_index.?;
724540 const seg = self.segments.items[seg_id];
725 const global = self.getEntryPoint() catch |err| switch (err) {
726 error.MissingMainEntrypoint => {
727 self.error_flags.no_entry_point_found = true;
728 break :blk;
729 },
730 else => |e| return e,
731 };
541 const global = self.getEntryPoint() orelse break :blk;
732542 const sym = self.getSymbol(global);
543
544 const addr: u64 = if (sym.undf())
545 // In this case, the symbol has been resolved in one of dylibs and so we point
546 // to the stub as its vmaddr value.
547 self.getStubsEntryAddress(global).?
548 else
549 sym.n_value;
550
733551 try lc_writer.writeStruct(macho.entry_point_command{
734 .entryoff = @as(u32, @intCast(sym.n_value - seg.vmaddr)),
552 .entryoff = @as(u32, @intCast(addr - seg.vmaddr)),
735553 .stacksize = self.base.options.stack_size_override orelse 0,
736554 });
737555 },
......@@ -745,21 +563,32 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No
745563 try lc_writer.writeStruct(macho.source_version_command{
746564 .version = 0,
747565 });
748 try load_commands.writeBuildVersionLC(&self.base.options, lc_writer);
566 {
567 const platform = Platform.fromTarget(self.base.options.target);
568 const sdk_version: ?std.SemanticVersion = if (self.base.options.sysroot) |path|
569 load_commands.inferSdkVersionFromSdkPath(path)
570 else
571 null;
572 if (platform.isBuildVersionCompatible()) {
573 try load_commands.writeBuildVersionLC(platform, sdk_version, lc_writer);
574 } else {
575 try load_commands.writeVersionMinLC(platform, sdk_version, lc_writer);
576 }
577 }
749578
750579 const uuid_cmd_offset = @sizeOf(macho.mach_header_64) + @as(u32, @intCast(lc_buffer.items.len));
751580 try lc_writer.writeStruct(self.uuid_cmd);
752581
753582 try load_commands.writeLoadDylibLCs(self.dylibs.items, self.referenced_dylibs.keys(), lc_writer);
754583
755 if (requires_codesig) {
584 if (codesig != null) {
756585 try lc_writer.writeStruct(self.codesig_cmd);
757586 }
758587
759588 const ncmds = load_commands.calcNumOfLCs(lc_buffer.items);
760589 try self.base.file.?.pwriteAll(lc_buffer.items, @sizeOf(macho.mach_header_64));
761590 try self.writeHeader(ncmds, @as(u32, @intCast(lc_buffer.items.len)));
762 try self.writeUuid(comp, uuid_cmd_offset, requires_codesig);
591 try self.writeUuid(comp, uuid_cmd_offset, codesig != null);
763592
764593 if (codesig) |*csig| {
765594 try self.writeCodeSignature(comp, csig); // code signing always comes last
......@@ -772,11 +601,6 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No
772601 try d_sym.flushModule(self);
773602 }
774603
775 // if (build_options.enable_link_snapshots) {
776 // if (self.base.options.enable_link_snapshots)
777 // try self.snapshotState();
778 // }
779
780604 if (cache_miss) {
781605 // Update the file with the digest. If it fails we can continue; it only
782606 // means that the next invocation will have an unnecessary cache miss.
......@@ -850,7 +674,7 @@ fn resolveLibSystemInDirs(arena: Allocator, dirs: []const []const u8, out_libs:
850674 // Try stub file first. If we hit it, then we're done as the stub file
851675 // re-exports every single symbol definition.
852676 for (dirs) |dir| {
853 if (try resolveLib(arena, dir, "System", ".tbd")) |full_path| {
677 if (try resolveLib(arena, dir, "libSystem", ".tbd")) |full_path| {
854678 try out_libs.put(full_path, .{ .needed = true, .weak = false, .path = full_path });
855679 return true;
856680 }
......@@ -858,8 +682,8 @@ fn resolveLibSystemInDirs(arena: Allocator, dirs: []const []const u8, out_libs:
858682 // If we didn't hit the stub file, try .dylib next. However, libSystem.dylib
859683 // doesn't export libc.dylib which we'll need to resolve subsequently also.
860684 for (dirs) |dir| {
861 if (try resolveLib(arena, dir, "System", ".dylib")) |libsystem_path| {
862 if (try resolveLib(arena, dir, "c", ".dylib")) |libc_path| {
685 if (try resolveLib(arena, dir, "libSystem", ".dylib")) |libsystem_path| {
686 if (try resolveLib(arena, dir, "libc", ".dylib")) |libc_path| {
863687 try out_libs.put(libsystem_path, .{ .needed = true, .weak = false, .path = libsystem_path });
864688 try out_libs.put(libc_path, .{ .needed = true, .weak = false, .path = libc_path });
865689 return true;
......@@ -876,7 +700,7 @@ fn resolveLib(
876700 name: []const u8,
877701 ext: []const u8,
878702) !?[]const u8 {
879 const search_name = try std.fmt.allocPrint(arena, "lib{s}{s}", .{ name, ext });
703 const search_name = try std.fmt.allocPrint(arena, "{s}{s}", .{ name, ext });
880704 const full_path = try fs.path.join(arena, &[_][]const u8{ search_dir, search_name });
881705
882706 // Check if the file exists.
......@@ -889,175 +713,388 @@ fn resolveLib(
889713 return full_path;
890714}
891715
892const ParseDylibError = error{
716const ParseError = error{
717 UnknownFileType,
718 InvalidTarget,
719 InvalidTargetFatLibrary,
720 DylibAlreadyExists,
721 IncompatibleDylibVersion,
893722 OutOfMemory,
894 EmptyStubFile,
895 MismatchedCpuArchitecture,
896 UnsupportedCpuArchitecture,
723 Overflow,
724 InputOutput,
725 MalformedArchive,
726 NotLibStub,
897727 EndOfStream,
898} || fs.File.OpenError || std.os.PReadError || Dylib.Id.ParseError;
899
900const DylibCreateOpts = struct {
901 syslibroot: ?[]const u8,
902 id: ?Dylib.Id = null,
903 dependent: bool = false,
904 needed: bool = false,
905 weak: bool = false,
906};
728 FileSystem,
729 NotSupported,
730} || std.os.SeekError || std.fs.File.OpenError || std.fs.File.ReadError || tapi.TapiError;
907731
908pub fn parseDylib(
732pub fn parsePositional(
909733 self: *MachO,
734 file: std.fs.File,
910735 path: []const u8,
736 must_link: bool,
911737 dependent_libs: anytype,
912 opts: DylibCreateOpts,
913) ParseDylibError!bool {
914 const gpa = self.base.allocator;
915 const file = fs.cwd().openFile(path, .{}) catch |err| switch (err) {
916 error.FileNotFound => return false,
917 else => |e| return e,
918 };
919 defer file.close();
738 ctx: *ParseErrorCtx,
739) ParseError!void {
740 const tracy = trace(@src());
741 defer tracy.end();
920742
921 const cpu_arch = self.base.options.target.cpu.arch;
922 const file_stat = try file.stat();
923 var file_size = math.cast(usize, file_stat.size) orelse return error.Overflow;
743 if (Object.isObject(file)) {
744 try self.parseObject(file, path, ctx);
745 } else {
746 try self.parseLibrary(file, path, .{
747 .path = null,
748 .needed = false,
749 .weak = false,
750 }, must_link, false, null, dependent_libs, ctx);
751 }
752}
924753
925 const reader = file.reader();
926 const fat_offset = math.cast(usize, try fat.getLibraryOffset(reader, cpu_arch)) orelse
927 return error.Overflow;
928 try file.seekTo(fat_offset);
929 file_size -= fat_offset;
754fn parseObject(
755 self: *MachO,
756 file: std.fs.File,
757 path: []const u8,
758 ctx: *ParseErrorCtx,
759) ParseError!void {
760 const tracy = trace(@src());
761 defer tracy.end();
930762
763 const gpa = self.base.allocator;
764 const mtime: u64 = mtime: {
765 const stat = file.stat() catch break :mtime 0;
766 break :mtime @as(u64, @intCast(@divFloor(stat.mtime, 1_000_000_000)));
767 };
768 const file_stat = try file.stat();
769 const file_size = math.cast(usize, file_stat.size) orelse return error.Overflow;
931770 const contents = try file.readToEndAllocOptions(gpa, file_size, file_size, @alignOf(u64), null);
932 defer gpa.free(contents);
933771
934 const dylib_id = @as(u16, @intCast(self.dylibs.items.len));
935 var dylib = Dylib{ .weak = opts.weak };
936
937 dylib.parseFromBinary(
938 gpa,
939 cpu_arch,
940 dylib_id,
941 dependent_libs,
942 path,
943 contents,
944 ) catch |err| switch (err) {
945 error.EndOfStream, error.NotDylib => {
946 try file.seekTo(0);
947
948 var lib_stub = LibStub.loadFromFile(gpa, file) catch {
949 dylib.deinit(gpa);
950 return false;
951 };
952 defer lib_stub.deinit();
953
954 try dylib.parseFromStub(
955 gpa,
956 self.base.options.target,
957 lib_stub,
958 dylib_id,
959 dependent_libs,
960 path,
961 );
962 },
963 else => |e| return e,
772 var object = Object{
773 .name = try gpa.dupe(u8, path),
774 .mtime = mtime,
775 .contents = contents,
964776 };
777 errdefer object.deinit(gpa);
778 try object.parse(gpa);
965779
966 if (opts.id) |id| {
967 if (dylib.id.?.current_version < id.compatibility_version) {
968 log.warn("found dylib is incompatible with the required minimum version", .{});
969 log.warn(" dylib: {s}", .{id.name});
970 log.warn(" required minimum version: {}", .{id.compatibility_version});
971 log.warn(" dylib version: {}", .{dylib.id.?.current_version});
780 const detected_cpu_arch: std.Target.Cpu.Arch = switch (object.header.cputype) {
781 macho.CPU_TYPE_ARM64 => .aarch64,
782 macho.CPU_TYPE_X86_64 => .x86_64,
783 else => unreachable,
784 };
785 const detected_platform = object.getPlatform();
786 const this_cpu_arch = self.base.options.target.cpu.arch;
787 const this_platform = Platform.fromTarget(self.base.options.target);
972788
973 // TODO maybe this should be an error and facilitate auto-cleanup?
974 dylib.deinit(gpa);
975 return false;
976 }
789 if (this_cpu_arch != detected_cpu_arch or
790 (detected_platform != null and !detected_platform.?.eqlTarget(this_platform)))
791 {
792 const platform = detected_platform orelse this_platform;
793 try ctx.detected_targets.append(try platform.allocPrintTarget(ctx.arena(), detected_cpu_arch));
794 return error.InvalidTarget;
977795 }
978796
979 try self.dylibs.append(gpa, dylib);
980 try self.dylibs_map.putNoClobber(gpa, dylib.id.?.name, dylib_id);
797 try self.objects.append(gpa, object);
798}
981799
982 const should_link_dylib_even_if_unreachable = blk: {
983 if (self.base.options.dead_strip_dylibs and !opts.needed) break :blk false;
984 break :blk !(opts.dependent or self.referenced_dylibs.contains(dylib_id));
985 };
800pub fn parseLibrary(
801 self: *MachO,
802 file: std.fs.File,
803 path: []const u8,
804 lib: link.SystemLib,
805 must_link: bool,
806 is_dependent: bool,
807 reexport_info: ?DylibReExportInfo,
808 dependent_libs: anytype,
809 ctx: *ParseErrorCtx,
810) ParseError!void {
811 const tracy = trace(@src());
812 defer tracy.end();
986813
987 if (should_link_dylib_even_if_unreachable) {
988 try self.referenced_dylibs.putNoClobber(gpa, dylib_id, {});
814 if (fat.isFatLibrary(file)) {
815 const offset = try self.parseFatLibrary(file, self.base.options.target.cpu.arch, ctx);
816 try file.seekTo(offset);
817
818 if (Archive.isArchive(file, offset)) {
819 try self.parseArchive(path, offset, must_link, ctx);
820 } else if (Dylib.isDylib(file, offset)) {
821 try self.parseDylib(file, path, offset, dependent_libs, .{
822 .needed = lib.needed,
823 .weak = lib.weak,
824 .dependent = is_dependent,
825 .reexport_info = reexport_info,
826 }, ctx);
827 } else return error.UnknownFileType;
828 } else if (Archive.isArchive(file, 0)) {
829 try self.parseArchive(path, 0, must_link, ctx);
830 } else if (Dylib.isDylib(file, 0)) {
831 try self.parseDylib(file, path, 0, dependent_libs, .{
832 .needed = lib.needed,
833 .weak = lib.weak,
834 .dependent = is_dependent,
835 .reexport_info = reexport_info,
836 }, ctx);
837 } else {
838 self.parseLibStub(file, path, dependent_libs, .{
839 .needed = lib.needed,
840 .weak = lib.weak,
841 .dependent = is_dependent,
842 .reexport_info = reexport_info,
843 }, ctx) catch |err| switch (err) {
844 error.NotLibStub, error.UnexpectedToken => return error.UnknownFileType,
845 else => |e| return e,
846 };
989847 }
848}
849
850pub fn parseFatLibrary(
851 self: *MachO,
852 file: std.fs.File,
853 cpu_arch: std.Target.Cpu.Arch,
854 ctx: *ParseErrorCtx,
855) ParseError!u64 {
856 const gpa = self.base.allocator;
990857
991 return true;
858 const fat_archs = try fat.parseArchs(gpa, file);
859 defer gpa.free(fat_archs);
860
861 const offset = for (fat_archs) |arch| {
862 if (arch.tag == cpu_arch) break arch.offset;
863 } else {
864 try ctx.detected_targets.ensureUnusedCapacity(fat_archs.len);
865 for (fat_archs) |arch| {
866 ctx.detected_targets.appendAssumeCapacity(try ctx.arena().dupe(u8, @tagName(arch.tag)));
867 }
868 return error.InvalidTargetFatLibrary;
869 };
870 return offset;
992871}
993872
994pub fn parseInputFiles(self: *MachO, files: []const []const u8, syslibroot: ?[]const u8, dependent_libs: anytype) !void {
995 for (files) |file_name| {
996 const full_path = full_path: {
997 var buffer: [fs.MAX_PATH_BYTES]u8 = undefined;
998 break :full_path try fs.realpath(file_name, &buffer);
873fn parseArchive(
874 self: *MachO,
875 path: []const u8,
876 fat_offset: u64,
877 must_link: bool,
878 ctx: *ParseErrorCtx,
879) ParseError!void {
880 const gpa = self.base.allocator;
881
882 // We take ownership of the file so that we can store it for the duration of symbol resolution.
883 // TODO we shouldn't need to do that and could pre-parse the archive like we do for zld/ELF?
884 const file = try std.fs.cwd().openFile(path, .{});
885 try file.seekTo(fat_offset);
886
887 var archive = Archive{
888 .file = file,
889 .fat_offset = fat_offset,
890 .name = try gpa.dupe(u8, path),
891 };
892 errdefer archive.deinit(gpa);
893
894 try archive.parse(gpa, file.reader());
895
896 // Verify arch and platform
897 if (archive.toc.values().len > 0) {
898 const offsets = archive.toc.values()[0].items;
899 assert(offsets.len > 0);
900 const off = offsets[0];
901 var object = try archive.parseObject(gpa, off); // TODO we are doing all this work to pull the header only!
902 defer object.deinit(gpa);
903
904 const detected_cpu_arch: std.Target.Cpu.Arch = switch (object.header.cputype) {
905 macho.CPU_TYPE_ARM64 => .aarch64,
906 macho.CPU_TYPE_X86_64 => .x86_64,
907 else => unreachable,
999908 };
1000 log.debug("parsing input file path '{s}'", .{full_path});
909 const detected_platform = object.getPlatform();
910 const this_cpu_arch = self.base.options.target.cpu.arch;
911 const this_platform = Platform.fromTarget(self.base.options.target);
1001912
1002 if (try self.parseObject(full_path)) continue;
1003 if (try self.parseArchive(full_path, false)) continue;
1004 if (try self.parseDylib(full_path, dependent_libs, .{
1005 .syslibroot = syslibroot,
1006 })) continue;
913 if (this_cpu_arch != detected_cpu_arch or
914 (detected_platform != null and !detected_platform.?.eqlTarget(this_platform)))
915 {
916 const platform = detected_platform orelse this_platform;
917 try ctx.detected_targets.append(try platform.allocPrintTarget(gpa, detected_cpu_arch));
918 return error.InvalidTarget;
919 }
920 }
1007921
1008 log.debug("unknown filetype for positional input file: '{s}'", .{file_name});
922 if (must_link) {
923 // Get all offsets from the ToC
924 var offsets = std.AutoArrayHashMap(u32, void).init(gpa);
925 defer offsets.deinit();
926 for (archive.toc.values()) |offs| {
927 for (offs.items) |off| {
928 _ = try offsets.getOrPut(off);
929 }
930 }
931 for (offsets.keys()) |off| {
932 const object = try archive.parseObject(gpa, off);
933 try self.objects.append(gpa, object);
934 }
935 } else {
936 try self.archives.append(gpa, archive);
1009937 }
1010938}
1011939
1012pub fn parseAndForceLoadStaticArchives(self: *MachO, files: []const []const u8) !void {
1013 for (files) |file_name| {
1014 const full_path = full_path: {
1015 var buffer: [fs.MAX_PATH_BYTES]u8 = undefined;
1016 break :full_path try fs.realpath(file_name, &buffer);
1017 };
1018 log.debug("parsing and force loading static archive '{s}'", .{full_path});
940pub const DylibReExportInfo = struct {
941 id: Dylib.Id,
942 parent: u16,
943};
944
945const DylibOpts = struct {
946 reexport_info: ?DylibReExportInfo = null,
947 dependent: bool = false,
948 needed: bool = false,
949 weak: bool = false,
950};
951
952fn parseDylib(
953 self: *MachO,
954 file: std.fs.File,
955 path: []const u8,
956 offset: u64,
957 dependent_libs: anytype,
958 dylib_options: DylibOpts,
959 ctx: *ParseErrorCtx,
960) ParseError!void {
961 const gpa = self.base.allocator;
962 const file_stat = try file.stat();
963 const file_size = math.cast(usize, file_stat.size - offset) orelse return error.Overflow;
964
965 const contents = try file.readToEndAllocOptions(gpa, file_size, file_size, @alignOf(u64), null);
966 defer gpa.free(contents);
967
968 var dylib = Dylib{ .path = try gpa.dupe(u8, path), .weak = dylib_options.weak };
969 errdefer dylib.deinit(gpa);
970
971 try dylib.parseFromBinary(
972 gpa,
973 @intCast(self.dylibs.items.len), // TODO defer it till later
974 dependent_libs,
975 path,
976 contents,
977 );
978
979 const detected_cpu_arch: std.Target.Cpu.Arch = switch (dylib.header.?.cputype) {
980 macho.CPU_TYPE_ARM64 => .aarch64,
981 macho.CPU_TYPE_X86_64 => .x86_64,
982 else => unreachable,
983 };
984 const detected_platform = dylib.getPlatform(contents);
985 const this_cpu_arch = self.base.options.target.cpu.arch;
986 const this_platform = Platform.fromTarget(self.base.options.target);
1019987
1020 if (try self.parseArchive(full_path, true)) continue;
1021 log.debug("unknown filetype: expected static archive: '{s}'", .{file_name});
988 if (this_cpu_arch != detected_cpu_arch or
989 (detected_platform != null and !detected_platform.?.eqlTarget(this_platform)))
990 {
991 const platform = detected_platform orelse this_platform;
992 try ctx.detected_targets.append(try platform.allocPrintTarget(ctx.arena(), detected_cpu_arch));
993 return error.InvalidTarget;
1022994 }
995
996 try self.addDylib(dylib, dylib_options, ctx);
1023997}
1024998
1025pub fn parseLibs(
999fn parseLibStub(
10261000 self: *MachO,
1027 lib_names: []const []const u8,
1028 lib_infos: []const link.SystemLib,
1029 syslibroot: ?[]const u8,
1001 file: std.fs.File,
1002 path: []const u8,
10301003 dependent_libs: anytype,
1031) !void {
1032 for (lib_names, 0..) |lib, i| {
1033 const lib_info = lib_infos[i];
1034 log.debug("parsing lib path '{s}'", .{lib});
1035 if (try self.parseDylib(lib, dependent_libs, .{
1036 .syslibroot = syslibroot,
1037 .needed = lib_info.needed,
1038 .weak = lib_info.weak,
1039 })) continue;
1004 dylib_options: DylibOpts,
1005 ctx: *ParseErrorCtx,
1006) ParseError!void {
1007 const gpa = self.base.allocator;
1008 var lib_stub = try LibStub.loadFromFile(gpa, file);
1009 defer lib_stub.deinit();
1010
1011 if (lib_stub.inner.len == 0) return error.NotLibStub;
1012
1013 // Verify target
1014 {
1015 var matcher = try Dylib.TargetMatcher.init(gpa, self.base.options.target);
1016 defer matcher.deinit();
1017
1018 const first_tbd = lib_stub.inner[0];
1019 const targets = try first_tbd.targets(gpa);
1020 defer {
1021 for (targets) |t| gpa.free(t);
1022 gpa.free(targets);
1023 }
1024 if (!matcher.matchesTarget(targets)) {
1025 try ctx.detected_targets.ensureUnusedCapacity(targets.len);
1026 for (targets) |t| {
1027 ctx.detected_targets.appendAssumeCapacity(try ctx.arena().dupe(u8, t));
1028 }
1029 return error.InvalidTarget;
1030 }
1031 }
1032
1033 var dylib = Dylib{ .path = try gpa.dupe(u8, path), .weak = dylib_options.weak };
1034 errdefer dylib.deinit(gpa);
1035
1036 try dylib.parseFromStub(
1037 gpa,
1038 self.base.options.target,
1039 lib_stub,
1040 @intCast(self.dylibs.items.len), // TODO defer it till later
1041 dependent_libs,
1042 path,
1043 );
1044
1045 try self.addDylib(dylib, dylib_options, ctx);
1046}
1047
1048fn addDylib(self: *MachO, dylib: Dylib, dylib_options: DylibOpts, ctx: *ParseErrorCtx) ParseError!void {
1049 if (dylib_options.reexport_info) |reexport_info| {
1050 if (dylib.id.?.current_version < reexport_info.id.compatibility_version) {
1051 ctx.detected_dylib_id = .{
1052 .parent = reexport_info.parent,
1053 .required_version = reexport_info.id.compatibility_version,
1054 .found_version = dylib.id.?.current_version,
1055 };
1056 return error.IncompatibleDylibVersion;
1057 }
1058 }
1059
1060 const gpa = self.base.allocator;
1061 const gop = try self.dylibs_map.getOrPut(gpa, dylib.id.?.name);
1062 if (gop.found_existing) return error.DylibAlreadyExists;
1063
1064 gop.value_ptr.* = @as(u16, @intCast(self.dylibs.items.len));
1065 try self.dylibs.append(gpa, dylib);
1066
1067 const should_link_dylib_even_if_unreachable = blk: {
1068 if (self.base.options.dead_strip_dylibs and !dylib_options.needed) break :blk false;
1069 break :blk !(dylib_options.dependent or self.referenced_dylibs.contains(gop.value_ptr.*));
1070 };
10401071
1041 log.debug("unknown filetype for a library: '{s}'", .{lib});
1072 if (should_link_dylib_even_if_unreachable) {
1073 try self.referenced_dylibs.putNoClobber(gpa, gop.value_ptr.*, {});
10421074 }
10431075}
10441076
1045pub fn parseDependentLibs(self: *MachO, syslibroot: ?[]const u8, dependent_libs: anytype) !void {
1077pub fn parseDependentLibs(self: *MachO, dependent_libs: anytype) !void {
1078 const tracy = trace(@src());
1079 defer tracy.end();
1080
10461081 // At this point, we can now parse dependents of dylibs preserving the inclusion order of:
10471082 // 1) anything on the linker line is parsed first
10481083 // 2) afterwards, we parse dependents of the included dylibs
10491084 // TODO this should not be performed if the user specifies `-flat_namespace` flag.
10501085 // See ld64 manpages.
1051 var arena_alloc = std.heap.ArenaAllocator.init(self.base.allocator);
1086 const gpa = self.base.allocator;
1087 var arena_alloc = std.heap.ArenaAllocator.init(gpa);
10521088 const arena = arena_alloc.allocator();
10531089 defer arena_alloc.deinit();
10541090
1055 while (dependent_libs.readItem()) |*dep_id| {
1056 defer dep_id.id.deinit(self.base.allocator);
1091 while (dependent_libs.readItem()) |dep_id| {
1092 defer dep_id.id.deinit(gpa);
10571093
10581094 if (self.dylibs_map.contains(dep_id.id.name)) continue;
10591095
1060 const weak = self.dylibs.items[dep_id.parent].weak;
1096 const parent = &self.dylibs.items[dep_id.parent];
1097 const weak = parent.weak;
10611098 const has_ext = blk: {
10621099 const basename = fs.path.basename(dep_id.id.name);
10631100 break :blk mem.lastIndexOfScalar(u8, basename, '.') != null;
......@@ -1068,22 +1105,45 @@ pub fn parseDependentLibs(self: *MachO, syslibroot: ?[]const u8, dependent_libs:
10681105 break :blk dep_id.id.name[0..index];
10691106 } else dep_id.id.name;
10701107
1071 for (&[_][]const u8{ extension, ".tbd" }) |ext| {
1072 const with_ext = try std.fmt.allocPrint(arena, "{s}{s}", .{ without_ext, ext });
1073 const full_path = if (syslibroot) |root| try fs.path.join(arena, &.{ root, with_ext }) else with_ext;
1108 const maybe_full_path = full_path: {
1109 if (self.base.options.sysroot) |root| {
1110 for (&[_][]const u8{ extension, ".tbd" }) |ext| {
1111 if (try resolveLib(arena, root, without_ext, ext)) |full_path| break :full_path full_path;
1112 }
1113 }
1114
1115 for (&[_][]const u8{ extension, ".tbd" }) |ext| {
1116 if (try resolveLib(arena, "", without_ext, ext)) |full_path| break :full_path full_path;
1117 }
10741118
1075 log.debug("trying dependency at fully resolved path {s}", .{full_path});
1119 break :full_path null;
1120 };
10761121
1077 const did_parse_successfully = try self.parseDylib(full_path, dependent_libs, .{
1078 .id = dep_id.id,
1079 .syslibroot = syslibroot,
1080 .dependent = true,
1081 .weak = weak,
1122 const full_path = maybe_full_path orelse {
1123 const parent_name = if (parent.id) |id| id.name else parent.path;
1124 try self.reportDependencyError(parent_name, null, "missing dynamic library dependency: '{s}'", .{
1125 dep_id.id.name,
10821126 });
1083 if (did_parse_successfully) break;
1084 } else {
1085 log.debug("unable to resolve dependency {s}", .{dep_id.id.name});
1086 }
1127 continue;
1128 };
1129
1130 const file = try std.fs.cwd().openFile(full_path, .{});
1131 defer file.close();
1132
1133 log.debug("parsing dependency {s} at fully resolved path {s}", .{ dep_id.id.name, full_path });
1134
1135 var parse_ctx = ParseErrorCtx.init(gpa);
1136 defer parse_ctx.deinit();
1137
1138 self.parseLibrary(file, full_path, .{
1139 .path = null,
1140 .needed = false,
1141 .weak = weak,
1142 }, false, true, dep_id, dependent_libs, &parse_ctx) catch |err|
1143 try self.handleAndReportParseError(full_path, err, &parse_ctx);
1144
1145 // TODO I think that it would be nice to rewrite this error to include metadata for failed dependency
1146 // in addition to parsing error
10871147 }
10881148}
10891149
......@@ -1172,7 +1232,7 @@ fn writeStubHelperPreamble(self: *MachO) !void {
11721232
11731233 const gpa = self.base.allocator;
11741234 const cpu_arch = self.base.options.target.cpu.arch;
1175 const size = stubs.calcStubHelperPreambleSize(cpu_arch);
1235 const size = stubs.stubHelperPreambleSize(cpu_arch);
11761236
11771237 var buf = try std.ArrayList(u8).initCapacity(gpa, size);
11781238 defer buf.deinit();
......@@ -1202,9 +1262,9 @@ fn writeStubTableEntry(self: *MachO, index: usize) !void {
12021262 const laptr_sect_id = self.la_symbol_ptr_section_index.?;
12031263
12041264 const cpu_arch = self.base.options.target.cpu.arch;
1205 const stub_entry_size = stubs.calcStubEntrySize(cpu_arch);
1206 const stub_helper_entry_size = stubs.calcStubHelperEntrySize(cpu_arch);
1207 const stub_helper_preamble_size = stubs.calcStubHelperPreambleSize(cpu_arch);
1265 const stub_entry_size = stubs.stubSize(cpu_arch);
1266 const stub_helper_entry_size = stubs.stubHelperSize(cpu_arch);
1267 const stub_helper_preamble_size = stubs.stubHelperPreambleSize(cpu_arch);
12081268
12091269 if (self.stub_table_count_dirty) {
12101270 // We grow all 3 sections one by one.
......@@ -1336,59 +1396,123 @@ pub fn allocateSpecialSymbols(self: *MachO) !void {
13361396 "__mh_execute_header",
13371397 }) |name| {
13381398 const global = self.getGlobal(name) orelse continue;
1339 if (global.file != null) continue;
1399 if (global.getFile() != null) continue;
13401400 const sym = self.getSymbolPtr(global);
13411401 const seg = self.getSegment(self.text_section_index.?);
1342 sym.n_sect = 1;
1402 sym.n_sect = self.text_section_index.? + 1;
13431403 sym.n_value = seg.vmaddr;
13441404
1345 log.debug("allocating {s} at the start of {s}", .{
1405 log.debug("allocating {s}(@0x{x},sect({d})) at the start of {s}", .{
13461406 name,
1407 sym.n_value,
1408 sym.n_sect,
13471409 seg.segName(),
13481410 });
13491411 }
13501412}
13511413
1352pub fn createAtom(self: *MachO) !Atom.Index {
1414const CreateAtomOpts = struct {
1415 size: u64 = 0,
1416 alignment: u32 = 0,
1417};
1418
1419pub fn createAtom(self: *MachO, sym_index: u32, opts: CreateAtomOpts) !Atom.Index {
13531420 const gpa = self.base.allocator;
1354 const atom_index = @as(Atom.Index, @intCast(self.atoms.items.len));
1421 const index = @as(Atom.Index, @intCast(self.atoms.items.len));
13551422 const atom = try self.atoms.addOne(gpa);
1356 const sym_index = try self.allocateSymbol();
1357 try self.atom_by_index_table.putNoClobber(gpa, sym_index, atom_index);
1358 atom.* = .{
1359 .sym_index = sym_index,
1360 .file = null,
1361 .size = 0,
1362 .prev_index = null,
1363 .next_index = null,
1364 };
1365 log.debug("creating ATOM(%{d}) at index {d}", .{ sym_index, atom_index });
1366 return atom_index;
1423 atom.* = .{};
1424 atom.sym_index = sym_index;
1425 atom.size = opts.size;
1426 atom.alignment = opts.alignment;
1427 log.debug("creating ATOM(%{d}) at index {d}", .{ sym_index, index });
1428 return index;
1429}
1430
1431pub fn createTentativeDefAtoms(self: *MachO) !void {
1432 const gpa = self.base.allocator;
1433
1434 for (self.globals.items) |global| {
1435 const sym = self.getSymbolPtr(global);
1436 if (!sym.tentative()) continue;
1437 if (sym.n_desc == N_DEAD) continue;
1438
1439 log.debug("creating tentative definition for ATOM(%{d}, '{s}') in object({?})", .{
1440 global.sym_index, self.getSymbolName(global), global.file,
1441 });
1442
1443 // Convert any tentative definition into a regular symbol and allocate
1444 // text blocks for each tentative definition.
1445 const size = sym.n_value;
1446 const alignment = (sym.n_desc >> 8) & 0x0f;
1447
1448 if (self.bss_section_index == null) {
1449 self.bss_section_index = try self.initSection("__DATA", "__bss", .{
1450 .flags = macho.S_ZEROFILL,
1451 });
1452 }
1453
1454 sym.* = .{
1455 .n_strx = sym.n_strx,
1456 .n_type = macho.N_SECT | macho.N_EXT,
1457 .n_sect = self.bss_section_index.? + 1,
1458 .n_desc = 0,
1459 .n_value = 0,
1460 };
1461
1462 const atom_index = try self.createAtom(global.sym_index, .{
1463 .size = size,
1464 .alignment = alignment,
1465 });
1466 const atom = self.getAtomPtr(atom_index);
1467 atom.file = global.file;
1468
1469 self.addAtomToSection(atom_index);
1470
1471 assert(global.getFile() != null);
1472 const object = &self.objects.items[global.getFile().?];
1473 try object.atoms.append(gpa, atom_index);
1474 object.atom_by_index_table[global.sym_index] = atom_index;
1475 }
13671476}
13681477
1369fn createDyldPrivateAtom(self: *MachO) !void {
1478pub fn createDyldPrivateAtom(self: *MachO) !void {
13701479 if (self.dyld_private_atom_index != null) return;
13711480
1372 const atom_index = try self.createAtom();
1373 const atom = self.getAtomPtr(atom_index);
1374 atom.size = @sizeOf(u64);
1481 const sym_index = try self.allocateSymbol();
1482 const atom_index = try self.createAtom(sym_index, .{
1483 .size = @sizeOf(u64),
1484 .alignment = 3,
1485 });
1486 try self.atom_by_index_table.putNoClobber(self.base.allocator, sym_index, atom_index);
1487
1488 if (self.data_section_index == null) {
1489 self.data_section_index = try self.initSection("__DATA", "__data", .{});
1490 }
13751491
1492 const atom = self.getAtom(atom_index);
13761493 const sym = atom.getSymbolPtr(self);
13771494 sym.n_type = macho.N_SECT;
13781495 sym.n_sect = self.data_section_index.? + 1;
13791496 self.dyld_private_atom_index = atom_index;
13801497
1381 sym.n_value = try self.allocateAtom(atom_index, atom.size, @alignOf(u64));
1382 log.debug("allocated dyld_private atom at 0x{x}", .{sym.n_value});
1383 var buffer: [@sizeOf(u64)]u8 = [_]u8{0} ** @sizeOf(u64);
1384 try self.writeAtom(atom_index, &buffer);
1498 switch (self.mode) {
1499 .zld => self.addAtomToSection(atom_index),
1500 .incremental => {
1501 sym.n_value = try self.allocateAtom(atom_index, atom.size, @alignOf(u64));
1502 log.debug("allocated dyld_private atom at 0x{x}", .{sym.n_value});
1503 var buffer: [@sizeOf(u64)]u8 = [_]u8{0} ** @sizeOf(u64);
1504 try self.writeAtom(atom_index, &buffer);
1505 },
1506 }
13851507}
13861508
13871509fn createThreadLocalDescriptorAtom(self: *MachO, sym_name: []const u8, target: SymbolWithLoc) !Atom.Index {
13881510 const gpa = self.base.allocator;
13891511 const size = 3 * @sizeOf(u64);
13901512 const required_alignment: u32 = 1;
1391 const atom_index = try self.createAtom();
1513 const sym_index = try self.allocateSymbol();
1514 const atom_index = try self.createAtom(sym_index, .{});
1515 try self.atom_by_index_table.putNoClobber(gpa, sym_index, atom_index);
13921516 self.getAtomPtr(atom_index).size = size;
13931517
13941518 const sym = self.getAtom(atom_index).getSymbolPtr(self);
......@@ -1415,16 +1539,12 @@ fn createThreadLocalDescriptorAtom(self: *MachO, sym_name: []const u8, target: S
14151539 return atom_index;
14161540}
14171541
1418fn createMhExecuteHeaderSymbol(self: *MachO) !void {
1542pub fn createMhExecuteHeaderSymbol(self: *MachO) !void {
14191543 if (self.base.options.output_mode != .Exe) return;
1420 if (self.getGlobal("__mh_execute_header")) |global| {
1421 const sym = self.getSymbol(global);
1422 if (!sym.undf() and !(sym.pext() or sym.weakDef())) return;
1423 }
14241544
14251545 const gpa = self.base.allocator;
14261546 const sym_index = try self.allocateSymbol();
1427 const sym_loc = SymbolWithLoc{ .sym_index = sym_index, .file = null };
1547 const sym_loc = SymbolWithLoc{ .sym_index = sym_index };
14281548 const sym = self.getSymbolPtr(sym_loc);
14291549 sym.* = .{
14301550 .n_strx = try self.strtab.insert(gpa, "__mh_execute_header"),
......@@ -1435,16 +1555,23 @@ fn createMhExecuteHeaderSymbol(self: *MachO) !void {
14351555 };
14361556
14371557 const gop = try self.getOrPutGlobalPtr("__mh_execute_header");
1558 if (gop.found_existing) {
1559 const global = gop.value_ptr.*;
1560 if (global.getFile()) |file| {
1561 const global_object = &self.objects.items[file];
1562 global_object.globals_lookup[global.sym_index] = self.getGlobalIndex("__mh_execute_header").?;
1563 }
1564 }
14381565 gop.value_ptr.* = sym_loc;
14391566}
14401567
1441fn createDsoHandleSymbol(self: *MachO) !void {
1568pub fn createDsoHandleSymbol(self: *MachO) !void {
14421569 const global = self.getGlobalPtr("___dso_handle") orelse return;
14431570 if (!self.getSymbol(global.*).undf()) return;
14441571
14451572 const gpa = self.base.allocator;
14461573 const sym_index = try self.allocateSymbol();
1447 const sym_loc = SymbolWithLoc{ .sym_index = sym_index, .file = null };
1574 const sym_loc = SymbolWithLoc{ .sym_index = sym_index };
14481575 const sym = self.getSymbolPtr(sym_loc);
14491576 sym.* = .{
14501577 .n_strx = try self.strtab.insert(gpa, "___dso_handle"),
......@@ -1453,10 +1580,51 @@ fn createDsoHandleSymbol(self: *MachO) !void {
14531580 .n_desc = macho.N_WEAK_DEF,
14541581 .n_value = 0,
14551582 };
1583 const global_index = self.getGlobalIndex("___dso_handle").?;
1584 if (global.getFile()) |file| {
1585 const global_object = &self.objects.items[file];
1586 global_object.globals_lookup[global.sym_index] = global_index;
1587 }
14561588 global.* = sym_loc;
14571589 _ = self.unresolved.swapRemove(self.getGlobalIndex("___dso_handle").?);
14581590}
14591591
1592pub fn resolveSymbols(self: *MachO, actions: *std.ArrayList(ResolveAction)) !void {
1593 // We add the specified entrypoint as the first unresolved symbols so that
1594 // we search for it in libraries should there be no object files specified
1595 // on the linker line.
1596 if (self.base.options.output_mode == .Exe) {
1597 const entry_name = self.base.options.entry orelse load_commands.default_entry_point;
1598 _ = try self.addUndefined(entry_name, .none);
1599 }
1600
1601 // Force resolution of any symbols requested by the user.
1602 for (self.base.options.force_undefined_symbols.keys()) |sym_name| {
1603 _ = try self.addUndefined(sym_name, .none);
1604 }
1605
1606 for (self.objects.items, 0..) |_, object_id| {
1607 try self.resolveSymbolsInObject(@as(u32, @intCast(object_id)));
1608 }
1609
1610 try self.resolveSymbolsInArchives();
1611
1612 // Finally, force resolution of dyld_stub_binder if there are imports
1613 // requested.
1614 if (self.unresolved.count() > 0 and self.dyld_stub_binder_index == null) {
1615 self.dyld_stub_binder_index = try self.addUndefined("dyld_stub_binder", .add_got);
1616 }
1617 if (!self.base.options.single_threaded and self.mode == .incremental) {
1618 _ = try self.addUndefined("__tlv_bootstrap", .none);
1619 }
1620
1621 try self.resolveSymbolsInDylibs(actions);
1622
1623 try self.createMhExecuteHeaderSymbol();
1624 try self.createDsoHandleSymbol();
1625 try self.resolveSymbolsAtLoading();
1626}
1627
14601628fn resolveGlobalSymbol(self: *MachO, current: SymbolWithLoc) !void {
14611629 const gpa = self.base.allocator;
14621630 const sym = self.getSymbol(current);
......@@ -1470,6 +1638,7 @@ fn resolveGlobalSymbol(self: *MachO, current: SymbolWithLoc) !void {
14701638 }
14711639 return;
14721640 }
1641 const global_index = self.getGlobalIndex(sym_name).?;
14731642 const global = gop.value_ptr.*;
14741643 const global_sym = self.getSymbol(global);
14751644
......@@ -1500,7 +1669,20 @@ fn resolveGlobalSymbol(self: *MachO, current: SymbolWithLoc) !void {
15001669 const sym_is_weak = sym.sect() and (sym.weakDef() or sym.pext());
15011670 const global_is_weak = global_sym.sect() and (global_sym.weakDef() or global_sym.pext());
15021671
1503 if (sym_is_strong and global_is_strong) return error.MultipleSymbolDefinitions;
1672 if (sym_is_strong and global_is_strong) {
1673 // TODO redo this logic with corresponding logic in updateDeclExports to avoid this
1674 // ugly check.
1675 if (self.mode == .zld) {
1676 try self.reportSymbolCollision(global, current);
1677 }
1678 return error.MultipleSymbolDefinitions;
1679 }
1680
1681 if (current.getFile()) |file| {
1682 const object = &self.objects.items[file];
1683 object.globals_lookup[current.sym_index] = global_index;
1684 }
1685
15041686 if (global_is_strong) return;
15051687 if (sym_is_weak and global_is_weak) return;
15061688 if (sym.tentative() and global_sym.tentative()) {
......@@ -1508,11 +1690,82 @@ fn resolveGlobalSymbol(self: *MachO, current: SymbolWithLoc) !void {
15081690 }
15091691 if (sym.undf() and !sym.tentative()) return;
15101692
1511 _ = self.unresolved.swapRemove(self.getGlobalIndex(sym_name).?);
1693 if (global.getFile()) |file| {
1694 const global_object = &self.objects.items[file];
1695 global_object.globals_lookup[global.sym_index] = global_index;
1696 }
1697 _ = self.unresolved.swapRemove(global_index);
15121698
15131699 gop.value_ptr.* = current;
15141700}
15151701
1702fn resolveSymbolsInObject(self: *MachO, object_id: u32) !void {
1703 const object = &self.objects.items[object_id];
1704 const in_symtab = object.in_symtab orelse return;
1705
1706 log.debug("resolving symbols in '{s}'", .{object.name});
1707
1708 var sym_index: u32 = 0;
1709 while (sym_index < in_symtab.len) : (sym_index += 1) {
1710 const sym = &object.symtab[sym_index];
1711 const sym_name = object.getSymbolName(sym_index);
1712 const sym_with_loc = SymbolWithLoc{
1713 .sym_index = sym_index,
1714 .file = object_id + 1,
1715 };
1716
1717 if (sym.stab() or sym.indr() or sym.abs()) {
1718 try self.reportUnhandledSymbolType(sym_with_loc);
1719 continue;
1720 }
1721
1722 if (sym.sect() and !sym.ext()) {
1723 log.debug("symbol '{s}' local to object {s}; skipping...", .{
1724 sym_name,
1725 object.name,
1726 });
1727 continue;
1728 }
1729
1730 self.resolveGlobalSymbol(.{
1731 .sym_index = sym_index,
1732 .file = object_id + 1,
1733 }) catch |err| switch (err) {
1734 error.MultipleSymbolDefinitions => return error.FlushFailure,
1735 else => |e| return e,
1736 };
1737 }
1738}
1739
1740fn resolveSymbolsInArchives(self: *MachO) !void {
1741 if (self.archives.items.len == 0) return;
1742
1743 const gpa = self.base.allocator;
1744 var next_sym: usize = 0;
1745 loop: while (next_sym < self.unresolved.count()) {
1746 const global = self.globals.items[self.unresolved.keys()[next_sym]];
1747 const sym_name = self.getSymbolName(global);
1748
1749 for (self.archives.items) |archive| {
1750 // Check if the entry exists in a static archive.
1751 const offsets = archive.toc.get(sym_name) orelse {
1752 // No hit.
1753 continue;
1754 };
1755 assert(offsets.items.len > 0);
1756
1757 const object_id = @as(u16, @intCast(self.objects.items.len));
1758 const object = try archive.parseObject(gpa, offsets.items[0]);
1759 try self.objects.append(gpa, object);
1760 try self.resolveSymbolsInObject(object_id);
1761
1762 continue :loop;
1763 }
1764
1765 next_sym += 1;
1766 }
1767}
1768
15161769fn resolveSymbolsInDylibs(self: *MachO, actions: *std.ArrayList(ResolveAction)) !void {
15171770 if (self.dylibs.items.len == 0) return;
15181771
......@@ -1542,6 +1795,7 @@ fn resolveSymbolsInDylibs(self: *MachO, actions: *std.ArrayList(ResolveAction))
15421795
15431796 if (self.unresolved.fetchSwapRemove(global_index)) |entry| blk: {
15441797 if (!sym.undf()) break :blk;
1798 if (self.mode == .zld) break :blk;
15451799 try actions.append(.{ .kind = entry.value, .target = global });
15461800 }
15471801
......@@ -1552,6 +1806,42 @@ fn resolveSymbolsInDylibs(self: *MachO, actions: *std.ArrayList(ResolveAction))
15521806 }
15531807}
15541808
1809fn resolveSymbolsAtLoading(self: *MachO) !void {
1810 const is_lib = self.base.options.output_mode == .Lib;
1811 const is_dyn_lib = self.base.options.link_mode == .Dynamic and is_lib;
1812 const allow_undef = is_dyn_lib and (self.base.options.allow_shlib_undefined orelse false);
1813
1814 var next_sym: usize = 0;
1815 while (next_sym < self.unresolved.count()) {
1816 const global_index = self.unresolved.keys()[next_sym];
1817 const global = self.globals.items[global_index];
1818 const sym = self.getSymbolPtr(global);
1819
1820 if (sym.discarded()) {
1821 sym.* = .{
1822 .n_strx = 0,
1823 .n_type = macho.N_UNDF,
1824 .n_sect = 0,
1825 .n_desc = 0,
1826 .n_value = 0,
1827 };
1828 _ = self.unresolved.swapRemove(global_index);
1829 continue;
1830 } else if (allow_undef) {
1831 const n_desc = @as(
1832 u16,
1833 @bitCast(macho.BIND_SPECIAL_DYLIB_FLAT_LOOKUP * @as(i16, @intCast(macho.N_SYMBOL_RESOLVER))),
1834 );
1835 sym.n_type = macho.N_EXT;
1836 sym.n_desc = n_desc;
1837 _ = self.unresolved.swapRemove(global_index);
1838 continue;
1839 }
1840
1841 next_sym += 1;
1842 }
1843}
1844
15551845pub fn deinit(self: *MachO) void {
15561846 const gpa = self.base.allocator;
15571847
......@@ -1563,8 +1853,15 @@ pub fn deinit(self: *MachO) void {
15631853
15641854 self.got_table.deinit(gpa);
15651855 self.stub_table.deinit(gpa);
1566 self.strtab.deinit(gpa);
1856 self.tlv_ptr_table.deinit(gpa);
1857 self.thunk_table.deinit(gpa);
15671858
1859 for (self.thunks.items) |*thunk| {
1860 thunk.deinit(gpa);
1861 }
1862 self.thunks.deinit(gpa);
1863
1864 self.strtab.deinit(gpa);
15681865 self.locals.deinit(gpa);
15691866 self.globals.deinit(gpa);
15701867 self.locals_free_list.deinit(gpa);
......@@ -1579,6 +1876,14 @@ pub fn deinit(self: *MachO) void {
15791876 self.resolver.deinit(gpa);
15801877 }
15811878
1879 for (self.objects.items) |*object| {
1880 object.deinit(gpa);
1881 }
1882 self.objects.deinit(gpa);
1883 for (self.archives.items) |*archive| {
1884 archive.deinit(gpa);
1885 }
1886 self.archives.deinit(gpa);
15821887 for (self.dylibs.items) |*dylib| {
15831888 dylib.deinit(gpa);
15841889 }
......@@ -1623,6 +1928,11 @@ pub fn deinit(self: *MachO) void {
16231928 bindings.deinit(gpa);
16241929 }
16251930 self.bindings.deinit(gpa);
1931
1932 for (self.misc_errors.items) |*err| {
1933 err.deinit(gpa);
1934 }
1935 self.misc_errors.deinit(gpa);
16261936}
16271937
16281938fn freeAtom(self: *MachO, atom_index: Atom.Index) void {
......@@ -1717,7 +2027,7 @@ fn growAtom(self: *MachO, atom_index: Atom.Index, new_atom_size: u64, alignment:
17172027 return self.allocateAtom(atom_index, new_atom_size, alignment);
17182028}
17192029
1720fn allocateSymbol(self: *MachO) !u32 {
2030pub fn allocateSymbol(self: *MachO) !u32 {
17212031 try self.locals.ensureUnusedCapacity(self.base.allocator, 1);
17222032
17232033 const index = blk: {
......@@ -1758,31 +2068,63 @@ fn allocateGlobal(self: *MachO) !u32 {
17582068 }
17592069 };
17602070
1761 self.globals.items[index] = .{
1762 .sym_index = 0,
1763 .file = null,
1764 };
2071 self.globals.items[index] = .{ .sym_index = 0 };
17652072
17662073 return index;
17672074}
17682075
1769fn addGotEntry(self: *MachO, target: SymbolWithLoc) !void {
2076pub fn addGotEntry(self: *MachO, target: SymbolWithLoc) !void {
17702077 if (self.got_table.lookup.contains(target)) return;
17712078 const got_index = try self.got_table.allocateEntry(self.base.allocator, target);
1772 try self.writeOffsetTableEntry(got_index);
1773 self.got_table_count_dirty = true;
1774 self.markRelocsDirtyByTarget(target);
2079 if (self.got_section_index == null) {
2080 self.got_section_index = try self.initSection("__DATA_CONST", "__got", .{
2081 .flags = macho.S_NON_LAZY_SYMBOL_POINTERS,
2082 });
2083 }
2084 if (self.mode == .incremental) {
2085 try self.writeOffsetTableEntry(got_index);
2086 self.got_table_count_dirty = true;
2087 self.markRelocsDirtyByTarget(target);
2088 }
17752089}
17762090
1777fn addStubEntry(self: *MachO, target: SymbolWithLoc) !void {
2091pub fn addStubEntry(self: *MachO, target: SymbolWithLoc) !void {
17782092 if (self.stub_table.lookup.contains(target)) return;
17792093 const stub_index = try self.stub_table.allocateEntry(self.base.allocator, target);
1780 try self.writeStubTableEntry(stub_index);
1781 self.stub_table_count_dirty = true;
1782 self.markRelocsDirtyByTarget(target);
1783}
1784
1785pub fn updateFunc(self: *MachO, mod: *Module, func_index: InternPool.Index, air: Air, liveness: Liveness) !void {
2094 if (self.stubs_section_index == null) {
2095 self.stubs_section_index = try self.initSection("__TEXT", "__stubs", .{
2096 .flags = macho.S_SYMBOL_STUBS |
2097 macho.S_ATTR_PURE_INSTRUCTIONS |
2098 macho.S_ATTR_SOME_INSTRUCTIONS,
2099 .reserved2 = stubs.stubSize(self.base.options.target.cpu.arch),
2100 });
2101 self.stub_helper_section_index = try self.initSection("__TEXT", "__stub_helper", .{
2102 .flags = macho.S_REGULAR |
2103 macho.S_ATTR_PURE_INSTRUCTIONS |
2104 macho.S_ATTR_SOME_INSTRUCTIONS,
2105 });
2106 self.la_symbol_ptr_section_index = try self.initSection("__DATA", "__la_symbol_ptr", .{
2107 .flags = macho.S_LAZY_SYMBOL_POINTERS,
2108 });
2109 }
2110 if (self.mode == .incremental) {
2111 try self.writeStubTableEntry(stub_index);
2112 self.stub_table_count_dirty = true;
2113 self.markRelocsDirtyByTarget(target);
2114 }
2115}
2116
2117pub fn addTlvPtrEntry(self: *MachO, target: SymbolWithLoc) !void {
2118 if (self.tlv_ptr_table.lookup.contains(target)) return;
2119 _ = try self.tlv_ptr_table.allocateEntry(self.base.allocator, target);
2120 if (self.tlv_ptr_section_index == null) {
2121 self.tlv_ptr_section_index = try self.initSection("__DATA", "__thread_ptrs", .{
2122 .flags = macho.S_THREAD_LOCAL_VARIABLE_POINTERS,
2123 });
2124 }
2125}
2126
2127pub fn updateFunc(self: *MachO, mod: *Module, func_index: InternPool.Index, air: Air, liveness: Liveness) !void {
17862128 if (build_options.skip_non_native and builtin.object_format != .macho) {
17872129 @panic("Attempted to compile for object format that was disabled by build configuration");
17882130 }
......@@ -1866,7 +2208,9 @@ pub fn lowerUnnamedConst(self: *MachO, typed_value: TypedValue, decl_index: Modu
18662208
18672209 log.debug("allocating symbol indexes for {s}", .{name});
18682210
1869 const atom_index = try self.createAtom();
2211 const sym_index = try self.allocateSymbol();
2212 const atom_index = try self.createAtom(sym_index, .{});
2213 try self.atom_by_index_table.putNoClobber(gpa, sym_index, atom_index);
18702214
18712215 const res = try codegen.generateSymbol(&self.base, decl.srcLoc(mod), typed_value, &code_buffer, .none, .{
18722216 .parent_atom_index = self.getAtom(atom_index).getSymbolIndex().?,
......@@ -1876,7 +2220,7 @@ pub fn lowerUnnamedConst(self: *MachO, typed_value: TypedValue, decl_index: Modu
18762220 .fail => |em| {
18772221 decl.analysis = .codegen_failure;
18782222 try mod.failed_decls.put(mod.gpa, decl_index, em);
1879 log.err("{s}", .{em.msg});
2223 log.debug("{s}", .{em.msg});
18802224 return error.CodegenFail;
18812225 },
18822226 };
......@@ -2031,7 +2375,7 @@ fn updateLazySymbolAtom(
20312375 const code = switch (res) {
20322376 .ok => code_buffer.items,
20332377 .fail => |em| {
2034 log.err("{s}", .{em.msg});
2378 log.debug("{s}", .{em.msg});
20352379 return error.CodegenFail;
20362380 },
20372381 };
......@@ -2068,7 +2412,11 @@ pub fn getOrCreateAtomForLazySymbol(self: *MachO, sym: File.LazySymbol) !Atom.In
20682412 },
20692413 };
20702414 switch (metadata.state.*) {
2071 .unused => metadata.atom.* = try self.createAtom(),
2415 .unused => {
2416 const sym_index = try self.allocateSymbol();
2417 metadata.atom.* = try self.createAtom(sym_index, .{});
2418 try self.atom_by_index_table.putNoClobber(self.base.allocator, sym_index, metadata.atom.*);
2419 },
20722420 .pending_flush => return metadata.atom.*,
20732421 .flushed => {},
20742422 }
......@@ -2180,8 +2528,11 @@ fn updateThreadlocalVariable(self: *MachO, module: *Module, decl_index: Module.D
21802528pub fn getOrCreateAtomForDecl(self: *MachO, decl_index: Module.Decl.Index) !Atom.Index {
21812529 const gop = try self.decls.getOrPut(self.base.allocator, decl_index);
21822530 if (!gop.found_existing) {
2531 const sym_index = try self.allocateSymbol();
2532 const atom_index = try self.createAtom(sym_index, .{});
2533 try self.atom_by_index_table.putNoClobber(self.base.allocator, sym_index, atom_index);
21832534 gop.value_ptr.* = .{
2184 .atom = try self.createAtom(),
2535 .atom = atom_index,
21852536 .section = self.getDeclOutputSection(decl_index),
21862537 .exports = .{},
21872538 };
......@@ -2373,7 +2724,7 @@ pub fn updateDeclExports(
23732724 try decl_metadata.exports.append(gpa, sym_index);
23742725 break :blk sym_index;
23752726 };
2376 const sym_loc = SymbolWithLoc{ .sym_index = sym_index, .file = null };
2727 const sym_loc = SymbolWithLoc{ .sym_index = sym_index };
23772728 const sym = self.getSymbolPtr(sym_loc);
23782729 sym.* = .{
23792730 .n_strx = try self.strtab.insert(gpa, exp_name),
......@@ -2403,7 +2754,7 @@ pub fn updateDeclExports(
24032754 error.MultipleSymbolDefinitions => {
24042755 // TODO: this needs rethinking
24052756 const global = self.getGlobal(exp_name).?;
2406 if (sym_loc.sym_index != global.sym_index and global.file != null) {
2757 if (sym_loc.sym_index != global.sym_index and global.getFile() != null) {
24072758 _ = try mod.failed_exports.put(mod.gpa, exp, try Module.ErrorMsg.create(
24082759 gpa,
24092760 decl.srcLoc(mod),
......@@ -2432,7 +2783,7 @@ pub fn deleteDeclExport(
24322783 defer gpa.free(exp_name);
24332784 const sym_index = metadata.getExportPtr(self, exp_name) orelse return;
24342785
2435 const sym_loc = SymbolWithLoc{ .sym_index = sym_index.*, .file = null };
2786 const sym_loc = SymbolWithLoc{ .sym_index = sym_index.* };
24362787 const sym = self.getSymbolPtr(sym_loc);
24372788 log.debug("deleting export '{s}'", .{exp_name});
24382789 assert(sym.sect() and sym.ext());
......@@ -2448,10 +2799,7 @@ pub fn deleteDeclExport(
24482799 if (self.resolver.fetchRemove(exp_name)) |entry| {
24492800 defer gpa.free(entry.key);
24502801 self.globals_free_list.append(gpa, entry.value) catch {};
2451 self.globals.items[entry.value] = .{
2452 .sym_index = 0,
2453 .file = null,
2454 };
2802 self.globals.items[entry.value] = .{ .sym_index = 0 };
24552803 }
24562804
24572805 sym_index.* = 0;
......@@ -2490,10 +2838,10 @@ pub fn getDeclVAddr(self: *MachO, decl_index: Module.Decl.Index, reloc_info: Fil
24902838
24912839 const this_atom_index = try self.getOrCreateAtomForDecl(decl_index);
24922840 const sym_index = self.getAtom(this_atom_index).getSymbolIndex().?;
2493 const atom_index = self.getAtomIndexForSymbol(.{ .sym_index = reloc_info.parent_atom_index, .file = null }).?;
2841 const atom_index = self.getAtomIndexForSymbol(.{ .sym_index = reloc_info.parent_atom_index }).?;
24942842 try Atom.addRelocation(self, atom_index, .{
24952843 .type = .unsigned,
2496 .target = .{ .sym_index = sym_index, .file = null },
2844 .target = .{ .sym_index = sym_index },
24972845 .offset = @as(u32, @intCast(reloc_info.offset)),
24982846 .addend = reloc_info.addend,
24992847 .pcrel = false,
......@@ -2526,7 +2874,7 @@ fn populateMissingMetadata(self: *MachO) !void {
25262874 // The first __TEXT segment is immovable and covers MachO header and load commands.
25272875 self.header_segment_cmd_index = @as(u8, @intCast(self.segments.items.len));
25282876 const ideal_size = @max(self.base.options.headerpad_size orelse 0, default_headerpad_size);
2529 const needed_size = mem.alignForward(u64, padToIdeal(ideal_size), self.page_size);
2877 const needed_size = mem.alignForward(u64, padToIdeal(ideal_size), getPageSize(cpu_arch));
25302878
25312879 log.debug("found __TEXT segment (header-only) free space 0x{x} to 0x{x}", .{ 0, needed_size });
25322880
......@@ -2558,14 +2906,10 @@ fn populateMissingMetadata(self: *MachO) !void {
25582906 }
25592907
25602908 if (self.stubs_section_index == null) {
2561 const stub_size = stubs.calcStubEntrySize(cpu_arch);
2909 const stub_size = stubs.stubSize(cpu_arch);
25622910 self.stubs_section_index = try self.allocateSection("__TEXT2", "__stubs", .{
25632911 .size = stub_size,
2564 .alignment = switch (cpu_arch) {
2565 .x86_64 => 1,
2566 .aarch64 => @sizeOf(u32),
2567 else => unreachable, // unhandled architecture type
2568 },
2912 .alignment = stubs.stubAlignment(cpu_arch),
25692913 .flags = macho.S_SYMBOL_STUBS | macho.S_ATTR_PURE_INSTRUCTIONS | macho.S_ATTR_SOME_INSTRUCTIONS,
25702914 .reserved2 = stub_size,
25712915 .prot = macho.PROT.READ | macho.PROT.EXEC,
......@@ -2576,11 +2920,7 @@ fn populateMissingMetadata(self: *MachO) !void {
25762920 if (self.stub_helper_section_index == null) {
25772921 self.stub_helper_section_index = try self.allocateSection("__TEXT3", "__stub_helper", .{
25782922 .size = @sizeOf(u32),
2579 .alignment = switch (cpu_arch) {
2580 .x86_64 => 1,
2581 .aarch64 => @sizeOf(u32),
2582 else => unreachable, // unhandled architecture type
2583 },
2923 .alignment = stubs.stubAlignment(cpu_arch),
25842924 .flags = macho.S_REGULAR | macho.S_ATTR_PURE_INSTRUCTIONS | macho.S_ATTR_SOME_INSTRUCTIONS,
25852925 .prot = macho.PROT.READ | macho.PROT.EXEC,
25862926 });
......@@ -2663,7 +3003,8 @@ fn populateMissingMetadata(self: *MachO) !void {
26633003
26643004fn calcPagezeroSize(self: *MachO) u64 {
26653005 const pagezero_vmsize = self.base.options.pagezero_size orelse default_pagezero_vmsize;
2666 const aligned_pagezero_vmsize = mem.alignBackward(u64, pagezero_vmsize, self.page_size);
3006 const page_size = getPageSize(self.base.options.target.cpu.arch);
3007 const aligned_pagezero_vmsize = mem.alignBackward(u64, pagezero_vmsize, page_size);
26673008 if (self.base.options.output_mode == .Lib) return 0;
26683009 if (aligned_pagezero_vmsize == 0) return 0;
26693010 if (aligned_pagezero_vmsize != pagezero_vmsize) {
......@@ -2673,6 +3014,28 @@ fn calcPagezeroSize(self: *MachO) u64 {
26733014 return aligned_pagezero_vmsize;
26743015}
26753016
3017const InitSectionOpts = struct {
3018 flags: u32 = macho.S_REGULAR,
3019 reserved1: u32 = 0,
3020 reserved2: u32 = 0,
3021};
3022
3023pub fn initSection(self: *MachO, segname: []const u8, sectname: []const u8, opts: InitSectionOpts) !u8 {
3024 log.debug("creating section '{s},{s}'", .{ segname, sectname });
3025 const index = @as(u8, @intCast(self.sections.slice().len));
3026 try self.sections.append(self.base.allocator, .{
3027 .segment_index = undefined, // Segments will be created automatically later down the pipeline
3028 .header = .{
3029 .sectname = makeStaticString(sectname),
3030 .segname = makeStaticString(segname),
3031 .flags = opts.flags,
3032 .reserved1 = opts.reserved1,
3033 .reserved2 = opts.reserved2,
3034 },
3035 });
3036 return index;
3037}
3038
26763039fn allocateSection(self: *MachO, segname: []const u8, sectname: []const u8, opts: struct {
26773040 size: u64 = 0,
26783041 alignment: u32 = 0,
......@@ -2681,17 +3044,17 @@ fn allocateSection(self: *MachO, segname: []const u8, sectname: []const u8, opts
26813044 reserved2: u32 = 0,
26823045}) !u8 {
26833046 const gpa = self.base.allocator;
3047 const page_size = getPageSize(self.base.options.target.cpu.arch);
26843048 // In incremental context, we create one section per segment pairing. This way,
26853049 // we can move the segment in raw file as we please.
26863050 const segment_id = @as(u8, @intCast(self.segments.items.len));
2687 const section_id = @as(u8, @intCast(self.sections.slice().len));
26883051 const vmaddr = blk: {
26893052 const prev_segment = self.segments.items[segment_id - 1];
2690 break :blk mem.alignForward(u64, prev_segment.vmaddr + prev_segment.vmsize, self.page_size);
3053 break :blk mem.alignForward(u64, prev_segment.vmaddr + prev_segment.vmsize, page_size);
26913054 };
26923055 // We commit more memory than needed upfront so that we don't have to reallocate too soon.
2693 const vmsize = mem.alignForward(u64, opts.size, self.page_size);
2694 const off = self.findFreeSpace(opts.size, self.page_size);
3056 const vmsize = mem.alignForward(u64, opts.size, page_size);
3057 const off = self.findFreeSpace(opts.size, page_size);
26953058
26963059 log.debug("found {s},{s} free space 0x{x} to 0x{x} (0x{x} - 0x{x})", .{
26973060 segname,
......@@ -2715,23 +3078,19 @@ fn allocateSection(self: *MachO, segname: []const u8, sectname: []const u8, opts
27153078 .cmdsize = @sizeOf(macho.segment_command_64) + @sizeOf(macho.section_64),
27163079 };
27173080
2718 var section = macho.section_64{
2719 .sectname = makeStaticString(sectname),
2720 .segname = makeStaticString(segname),
2721 .addr = mem.alignForward(u64, vmaddr, opts.alignment),
2722 .offset = mem.alignForward(u32, @as(u32, @intCast(off)), opts.alignment),
2723 .size = opts.size,
2724 .@"align" = math.log2(opts.alignment),
3081 const sect_id = try self.initSection(segname, sectname, .{
27253082 .flags = opts.flags,
27263083 .reserved2 = opts.reserved2,
2727 };
3084 });
3085 const section = &self.sections.items(.header)[sect_id];
3086 section.addr = mem.alignForward(u64, vmaddr, opts.alignment);
3087 section.offset = mem.alignForward(u32, @as(u32, @intCast(off)), opts.alignment);
3088 section.size = opts.size;
3089 section.@"align" = math.log2(opts.alignment);
3090 self.sections.items(.segment_index)[sect_id] = segment_id;
27283091 assert(!section.isZerofill()); // TODO zerofill sections
27293092
2730 try self.sections.append(gpa, .{
2731 .segment_index = segment_id,
2732 .header = section,
2733 });
2734 return section_id;
3093 return sect_id;
27353094}
27363095
27373096fn growSection(self: *MachO, sect_id: u8, needed_size: u64) !void {
......@@ -2740,9 +3099,10 @@ fn growSection(self: *MachO, sect_id: u8, needed_size: u64) !void {
27403099 const segment = &self.segments.items[segment_index];
27413100 const maybe_last_atom_index = self.sections.items(.last_atom_index)[sect_id];
27423101 const sect_capacity = self.allocatedSize(header.offset);
3102 const page_size = getPageSize(self.base.options.target.cpu.arch);
27433103
27443104 if (needed_size > sect_capacity) {
2745 const new_offset = self.findFreeSpace(needed_size, self.page_size);
3105 const new_offset = self.findFreeSpace(needed_size, page_size);
27463106 const current_size = if (maybe_last_atom_index) |last_atom_index| blk: {
27473107 const last_atom = self.getAtom(last_atom_index);
27483108 const sym = last_atom.getSymbol(self);
......@@ -2774,16 +3134,17 @@ fn growSection(self: *MachO, sect_id: u8, needed_size: u64) !void {
27743134 }
27753135
27763136 header.size = needed_size;
2777 segment.filesize = mem.alignForward(u64, needed_size, self.page_size);
2778 segment.vmsize = mem.alignForward(u64, needed_size, self.page_size);
3137 segment.filesize = mem.alignForward(u64, needed_size, page_size);
3138 segment.vmsize = mem.alignForward(u64, needed_size, page_size);
27793139}
27803140
27813141fn growSectionVirtualMemory(self: *MachO, sect_id: u8, needed_size: u64) !void {
3142 const page_size = getPageSize(self.base.options.target.cpu.arch);
27823143 const header = &self.sections.items(.header)[sect_id];
27833144 const segment = self.getSegmentPtr(sect_id);
27843145 const increased_size = padToIdeal(needed_size);
27853146 const old_aligned_end = segment.vmaddr + segment.vmsize;
2786 const new_aligned_end = segment.vmaddr + mem.alignForward(u64, increased_size, self.page_size);
3147 const new_aligned_end = segment.vmaddr + mem.alignForward(u64, increased_size, page_size);
27873148 const diff = new_aligned_end - old_aligned_end;
27883149 log.debug("shifting every segment after {s},{s} in virtual memory by {x}", .{
27893150 header.segName(),
......@@ -2814,10 +3175,29 @@ fn growSectionVirtualMemory(self: *MachO, sect_id: u8, needed_size: u64) !void {
28143175 }
28153176}
28163177
3178pub fn addAtomToSection(self: *MachO, atom_index: Atom.Index) void {
3179 assert(self.mode == .zld);
3180 const atom = self.getAtomPtr(atom_index);
3181 const sym = self.getSymbol(atom.getSymbolWithLoc());
3182 var section = self.sections.get(sym.n_sect - 1);
3183 if (section.header.size > 0) {
3184 const last_atom = self.getAtomPtr(section.last_atom_index.?);
3185 last_atom.next_index = atom_index;
3186 atom.prev_index = section.last_atom_index;
3187 } else {
3188 section.first_atom_index = atom_index;
3189 }
3190 section.last_atom_index = atom_index;
3191 section.header.size += atom.size;
3192 self.sections.set(sym.n_sect - 1, section);
3193}
3194
28173195fn allocateAtom(self: *MachO, atom_index: Atom.Index, new_atom_size: u64, alignment: u64) !u64 {
28183196 const tracy = trace(@src());
28193197 defer tracy.end();
28203198
3199 assert(self.mode == .incremental);
3200
28213201 const atom = self.getAtom(atom_index);
28223202 const sect_id = atom.getSymbol(self).n_sect - 1;
28233203 const segment = self.getSegmentPtr(sect_id);
......@@ -2944,17 +3324,35 @@ pub fn getGlobalSymbol(self: *MachO, name: []const u8, lib_name: ?[]const u8) !u
29443324 return self.addUndefined(sym_name, .add_stub);
29453325}
29463326
2947fn writeSegmentHeaders(self: *MachO, writer: anytype) !void {
3327pub fn writeSegmentHeaders(self: *MachO, writer: anytype) !void {
29483328 for (self.segments.items, 0..) |seg, i| {
29493329 const indexes = self.getSectionIndexes(@as(u8, @intCast(i)));
2950 try writer.writeStruct(seg);
3330 var out_seg = seg;
3331 out_seg.cmdsize = @sizeOf(macho.segment_command_64);
3332 out_seg.nsects = 0;
3333
3334 // Update section headers count; any section with size of 0 is excluded
3335 // since it doesn't have any data in the final binary file.
3336 for (self.sections.items(.header)[indexes.start..indexes.end]) |header| {
3337 if (header.size == 0) continue;
3338 out_seg.cmdsize += @sizeOf(macho.section_64);
3339 out_seg.nsects += 1;
3340 }
3341
3342 if (out_seg.nsects == 0 and
3343 (mem.eql(u8, out_seg.segName(), "__DATA_CONST") or
3344 mem.eql(u8, out_seg.segName(), "__DATA"))) continue;
3345
3346 try writer.writeStruct(out_seg);
29513347 for (self.sections.items(.header)[indexes.start..indexes.end]) |header| {
3348 if (header.size == 0) continue;
29523349 try writer.writeStruct(header);
29533350 }
29543351 }
29553352}
29563353
2957fn writeLinkeditSegmentData(self: *MachO) !void {
3354pub fn writeLinkeditSegmentData(self: *MachO) !void {
3355 const page_size = getPageSize(self.base.options.target.cpu.arch);
29583356 const seg = self.getLinkeditSegmentPtr();
29593357 seg.filesize = 0;
29603358 seg.vmsize = 0;
......@@ -2962,27 +3360,33 @@ fn writeLinkeditSegmentData(self: *MachO) !void {
29623360 for (self.segments.items, 0..) |segment, id| {
29633361 if (self.linkedit_segment_cmd_index.? == @as(u8, @intCast(id))) continue;
29643362 if (seg.vmaddr < segment.vmaddr + segment.vmsize) {
2965 seg.vmaddr = mem.alignForward(u64, segment.vmaddr + segment.vmsize, self.page_size);
3363 seg.vmaddr = mem.alignForward(u64, segment.vmaddr + segment.vmsize, page_size);
29663364 }
29673365 if (seg.fileoff < segment.fileoff + segment.filesize) {
2968 seg.fileoff = mem.alignForward(u64, segment.fileoff + segment.filesize, self.page_size);
3366 seg.fileoff = mem.alignForward(u64, segment.fileoff + segment.filesize, page_size);
29693367 }
29703368 }
29713369
29723370 try self.writeDyldInfoData();
3371 // TODO handle this better
3372 if (self.mode == .zld) {
3373 try self.writeFunctionStarts();
3374 try self.writeDataInCode();
3375 }
29733376 try self.writeSymtabs();
29743377
2975 seg.vmsize = mem.alignForward(u64, seg.filesize, self.page_size);
3378 seg.vmsize = mem.alignForward(u64, seg.filesize, page_size);
29763379}
29773380
29783381fn collectRebaseDataFromTableSection(self: *MachO, sect_id: u8, rebase: *Rebase, table: anytype) !void {
3382 const gpa = self.base.allocator;
29793383 const header = self.sections.items(.header)[sect_id];
29803384 const segment_index = self.sections.items(.segment_index)[sect_id];
29813385 const segment = self.segments.items[segment_index];
29823386 const base_offset = header.addr - segment.vmaddr;
29833387 const is_got = if (self.got_section_index) |index| index == sect_id else false;
29843388
2985 try rebase.entries.ensureUnusedCapacity(self.base.allocator, table.entries.items.len);
3389 try rebase.entries.ensureUnusedCapacity(gpa, table.entries.items.len);
29863390
29873391 for (table.entries.items, 0..) |entry, i| {
29883392 if (!table.lookup.contains(entry)) continue;
......@@ -3024,19 +3428,96 @@ fn collectRebaseData(self: *MachO, rebase: *Rebase) !void {
30243428 }
30253429 }
30263430
3027 try self.collectRebaseDataFromTableSection(self.got_section_index.?, rebase, self.got_table);
3028 try self.collectRebaseDataFromTableSection(self.la_symbol_ptr_section_index.?, rebase, self.stub_table);
3431 // Unpack GOT entries
3432 if (self.got_section_index) |sect_id| {
3433 try self.collectRebaseDataFromTableSection(sect_id, rebase, self.got_table);
3434 }
3435
3436 // Next, unpack __la_symbol_ptr entries
3437 if (self.la_symbol_ptr_section_index) |sect_id| {
3438 try self.collectRebaseDataFromTableSection(sect_id, rebase, self.stub_table);
3439 }
3440
3441 // Finally, unpack the rest.
3442 const cpu_arch = self.base.options.target.cpu.arch;
3443 for (self.objects.items) |*object| {
3444 for (object.atoms.items) |atom_index| {
3445 const atom = self.getAtom(atom_index);
3446 const sym = self.getSymbol(atom.getSymbolWithLoc());
3447 if (sym.n_desc == N_DEAD) continue;
3448
3449 const sect_id = sym.n_sect - 1;
3450 const section = self.sections.items(.header)[sect_id];
3451 const segment_id = self.sections.items(.segment_index)[sect_id];
3452 const segment = self.segments.items[segment_id];
3453 if (segment.maxprot & macho.PROT.WRITE == 0) continue;
3454 switch (section.type()) {
3455 macho.S_LITERAL_POINTERS,
3456 macho.S_REGULAR,
3457 macho.S_MOD_INIT_FUNC_POINTERS,
3458 macho.S_MOD_TERM_FUNC_POINTERS,
3459 => {},
3460 else => continue,
3461 }
3462
3463 log.debug(" ATOM({d}, %{d}, '{s}')", .{
3464 atom_index,
3465 atom.sym_index,
3466 self.getSymbolName(atom.getSymbolWithLoc()),
3467 });
3468
3469 const code = Atom.getAtomCode(self, atom_index);
3470 const relocs = Atom.getAtomRelocs(self, atom_index);
3471 const ctx = Atom.getRelocContext(self, atom_index);
3472
3473 for (relocs) |rel| {
3474 switch (cpu_arch) {
3475 .aarch64 => {
3476 const rel_type = @as(macho.reloc_type_arm64, @enumFromInt(rel.r_type));
3477 if (rel_type != .ARM64_RELOC_UNSIGNED) continue;
3478 if (rel.r_length != 3) continue;
3479 },
3480 .x86_64 => {
3481 const rel_type = @as(macho.reloc_type_x86_64, @enumFromInt(rel.r_type));
3482 if (rel_type != .X86_64_RELOC_UNSIGNED) continue;
3483 if (rel.r_length != 3) continue;
3484 },
3485 else => unreachable,
3486 }
3487 const target = Atom.parseRelocTarget(self, .{
3488 .object_id = atom.getFile().?,
3489 .rel = rel,
3490 .code = code,
3491 .base_offset = ctx.base_offset,
3492 .base_addr = ctx.base_addr,
3493 });
3494 const target_sym = self.getSymbol(target);
3495 if (target_sym.undf()) continue;
3496
3497 const base_offset = @as(i32, @intCast(sym.n_value - segment.vmaddr));
3498 const rel_offset = rel.r_address - ctx.base_offset;
3499 const offset = @as(u64, @intCast(base_offset + rel_offset));
3500 log.debug(" | rebase at {x}", .{offset});
3501
3502 try rebase.entries.append(gpa, .{
3503 .offset = offset,
3504 .segment_id = segment_id,
3505 });
3506 }
3507 }
3508 }
30293509
30303510 try rebase.finalize(gpa);
30313511}
30323512
30333513fn collectBindDataFromTableSection(self: *MachO, sect_id: u8, bind: anytype, table: anytype) !void {
3514 const gpa = self.base.allocator;
30343515 const header = self.sections.items(.header)[sect_id];
30353516 const segment_index = self.sections.items(.segment_index)[sect_id];
30363517 const segment = self.segments.items[segment_index];
30373518 const base_offset = header.addr - segment.vmaddr;
30383519
3039 try bind.entries.ensureUnusedCapacity(self.base.allocator, table.entries.items.len);
3520 try bind.entries.ensureUnusedCapacity(gpa, table.entries.items.len);
30403521
30413522 for (table.entries.items, 0..) |entry, i| {
30423523 if (!table.lookup.contains(entry)) continue;
......@@ -3101,13 +3582,105 @@ fn collectBindData(self: *MachO, bind: anytype, raw_bindings: anytype) !void {
31013582 }
31023583 }
31033584
3104 // Gather GOT pointers
3105 try self.collectBindDataFromTableSection(self.got_section_index.?, bind, self.got_table);
3585 // Unpack GOT pointers
3586 if (self.got_section_index) |sect_id| {
3587 try self.collectBindDataFromTableSection(sect_id, bind, self.got_table);
3588 }
3589
3590 // Next, unpack TLV pointers section
3591 if (self.tlv_ptr_section_index) |sect_id| {
3592 try self.collectBindDataFromTableSection(sect_id, bind, self.tlv_ptr_table);
3593 }
3594
3595 // Finally, unpack the rest.
3596 const cpu_arch = self.base.options.target.cpu.arch;
3597 for (self.objects.items) |*object| {
3598 for (object.atoms.items) |atom_index| {
3599 const atom = self.getAtom(atom_index);
3600 const sym = self.getSymbol(atom.getSymbolWithLoc());
3601 if (sym.n_desc == N_DEAD) continue;
3602
3603 const sect_id = sym.n_sect - 1;
3604 const section = self.sections.items(.header)[sect_id];
3605 const segment_id = self.sections.items(.segment_index)[sect_id];
3606 const segment = self.segments.items[segment_id];
3607 if (segment.maxprot & macho.PROT.WRITE == 0) continue;
3608 switch (section.type()) {
3609 macho.S_LITERAL_POINTERS,
3610 macho.S_REGULAR,
3611 macho.S_MOD_INIT_FUNC_POINTERS,
3612 macho.S_MOD_TERM_FUNC_POINTERS,
3613 => {},
3614 else => continue,
3615 }
3616
3617 log.debug(" ATOM({d}, %{d}, '{s}')", .{
3618 atom_index,
3619 atom.sym_index,
3620 self.getSymbolName(atom.getSymbolWithLoc()),
3621 });
3622
3623 const code = Atom.getAtomCode(self, atom_index);
3624 const relocs = Atom.getAtomRelocs(self, atom_index);
3625 const ctx = Atom.getRelocContext(self, atom_index);
3626
3627 for (relocs) |rel| {
3628 switch (cpu_arch) {
3629 .aarch64 => {
3630 const rel_type = @as(macho.reloc_type_arm64, @enumFromInt(rel.r_type));
3631 if (rel_type != .ARM64_RELOC_UNSIGNED) continue;
3632 if (rel.r_length != 3) continue;
3633 },
3634 .x86_64 => {
3635 const rel_type = @as(macho.reloc_type_x86_64, @enumFromInt(rel.r_type));
3636 if (rel_type != .X86_64_RELOC_UNSIGNED) continue;
3637 if (rel.r_length != 3) continue;
3638 },
3639 else => unreachable,
3640 }
3641
3642 const global = Atom.parseRelocTarget(self, .{
3643 .object_id = atom.getFile().?,
3644 .rel = rel,
3645 .code = code,
3646 .base_offset = ctx.base_offset,
3647 .base_addr = ctx.base_addr,
3648 });
3649 const bind_sym_name = self.getSymbolName(global);
3650 const bind_sym = self.getSymbol(global);
3651 if (!bind_sym.undf()) continue;
3652
3653 const base_offset = sym.n_value - segment.vmaddr;
3654 const rel_offset = @as(u32, @intCast(rel.r_address - ctx.base_offset));
3655 const offset = @as(u64, @intCast(base_offset + rel_offset));
3656 const addend = mem.readIntLittle(i64, code[rel_offset..][0..8]);
3657
3658 const dylib_ordinal = @divTrunc(@as(i16, @bitCast(bind_sym.n_desc)), macho.N_SYMBOL_RESOLVER);
3659 log.debug(" | bind at {x}, import('{s}') in dylib({d})", .{
3660 base_offset,
3661 bind_sym_name,
3662 dylib_ordinal,
3663 });
3664 log.debug(" | with addend {x}", .{addend});
3665 if (bind_sym.weakRef()) {
3666 log.debug(" | marking as weak ref ", .{});
3667 }
3668 try bind.entries.append(gpa, .{
3669 .target = global,
3670 .offset = offset,
3671 .segment_id = segment_id,
3672 .addend = addend,
3673 });
3674 }
3675 }
3676 }
3677
31063678 try bind.finalize(gpa, self);
31073679}
31083680
31093681fn collectLazyBindData(self: *MachO, bind: anytype) !void {
3110 try self.collectBindDataFromTableSection(self.la_symbol_ptr_section_index.?, bind, self.stub_table);
3682 const sect_id = self.la_symbol_ptr_section_index orelse return;
3683 try self.collectBindDataFromTableSection(sect_id, bind, self.stub_table);
31113684 try bind.finalize(self.base.allocator, self);
31123685}
31133686
......@@ -3124,7 +3697,8 @@ fn collectExportData(self: *MachO, trie: *Trie) !void {
31243697 const sym = self.getSymbol(global);
31253698
31263699 if (sym.undf()) continue;
3127 if (!sym.ext()) continue;
3700 assert(sym.ext());
3701 if (sym.n_desc == N_DEAD) continue;
31283702
31293703 const sym_name = self.getSymbolName(global);
31303704 log.debug(" (putting '{s}' defined at 0x{x})", .{ sym_name, sym.n_value });
......@@ -3227,18 +3801,18 @@ fn writeDyldInfoData(self: *MachO) !void {
32273801 self.dyld_info_cmd.export_size = @as(u32, @intCast(export_size_aligned));
32283802}
32293803
3230fn populateLazyBindOffsetsInStubHelper(self: *MachO, lazy_bind: LazyBind) !void {
3804fn populateLazyBindOffsetsInStubHelper(self: *MachO, lazy_bind: anytype) !void {
32313805 if (lazy_bind.size() == 0) return;
32323806
32333807 const stub_helper_section_index = self.stub_helper_section_index.?;
3234 assert(self.stub_helper_preamble_allocated);
3808 // assert(ctx.stub_helper_preamble_allocated);
32353809
32363810 const header = self.sections.items(.header)[stub_helper_section_index];
32373811
32383812 const cpu_arch = self.base.options.target.cpu.arch;
3239 const preamble_size = stubs.calcStubHelperPreambleSize(cpu_arch);
3240 const stub_size = stubs.calcStubHelperEntrySize(cpu_arch);
3241 const stub_offset = stubs.calcStubOffsetInStubHelper(cpu_arch);
3813 const preamble_size = stubs.stubHelperPreambleSize(cpu_arch);
3814 const stub_size = stubs.stubHelperSize(cpu_arch);
3815 const stub_offset = stubs.stubOffsetInStubHelper(cpu_arch);
32423816 const base_offset = header.offset + preamble_size;
32433817
32443818 for (lazy_bind.offsets.items, 0..) |bind_offset, index| {
......@@ -3254,6 +3828,167 @@ fn populateLazyBindOffsetsInStubHelper(self: *MachO, lazy_bind: LazyBind) !void
32543828 }
32553829}
32563830
3831const asc_u64 = std.sort.asc(u64);
3832
3833fn addSymbolToFunctionStarts(self: *MachO, sym_loc: SymbolWithLoc, addresses: *std.ArrayList(u64)) !void {
3834 const sym = self.getSymbol(sym_loc);
3835 if (sym.n_strx == 0) return;
3836 if (sym.n_desc == MachO.N_DEAD) return;
3837 if (self.symbolIsTemp(sym_loc)) return;
3838 try addresses.append(sym.n_value);
3839}
3840
3841fn writeFunctionStarts(self: *MachO) !void {
3842 const gpa = self.base.allocator;
3843 const seg = self.segments.items[self.header_segment_cmd_index.?];
3844
3845 // We need to sort by address first
3846 var addresses = std.ArrayList(u64).init(gpa);
3847 defer addresses.deinit();
3848
3849 for (self.objects.items) |object| {
3850 for (object.exec_atoms.items) |atom_index| {
3851 const atom = self.getAtom(atom_index);
3852 const sym_loc = atom.getSymbolWithLoc();
3853 try self.addSymbolToFunctionStarts(sym_loc, &addresses);
3854
3855 var it = Atom.getInnerSymbolsIterator(self, atom_index);
3856 while (it.next()) |inner_sym_loc| {
3857 try self.addSymbolToFunctionStarts(inner_sym_loc, &addresses);
3858 }
3859 }
3860 }
3861
3862 mem.sort(u64, addresses.items, {}, asc_u64);
3863
3864 var offsets = std.ArrayList(u32).init(gpa);
3865 defer offsets.deinit();
3866 try offsets.ensureTotalCapacityPrecise(addresses.items.len);
3867
3868 var last_off: u32 = 0;
3869 for (addresses.items) |addr| {
3870 const offset = @as(u32, @intCast(addr - seg.vmaddr));
3871 const diff = offset - last_off;
3872
3873 if (diff == 0) continue;
3874
3875 offsets.appendAssumeCapacity(diff);
3876 last_off = offset;
3877 }
3878
3879 var buffer = std.ArrayList(u8).init(gpa);
3880 defer buffer.deinit();
3881
3882 const max_size = @as(usize, @intCast(offsets.items.len * @sizeOf(u64)));
3883 try buffer.ensureTotalCapacity(max_size);
3884
3885 for (offsets.items) |offset| {
3886 try std.leb.writeULEB128(buffer.writer(), offset);
3887 }
3888
3889 const link_seg = self.getLinkeditSegmentPtr();
3890 const offset = link_seg.fileoff + link_seg.filesize;
3891 assert(mem.isAlignedGeneric(u64, offset, @alignOf(u64)));
3892 const needed_size = buffer.items.len;
3893 const needed_size_aligned = mem.alignForward(u64, needed_size, @alignOf(u64));
3894 const padding = math.cast(usize, needed_size_aligned - needed_size) orelse return error.Overflow;
3895 if (padding > 0) {
3896 try buffer.ensureUnusedCapacity(padding);
3897 buffer.appendNTimesAssumeCapacity(0, padding);
3898 }
3899 link_seg.filesize = offset + needed_size_aligned - link_seg.fileoff;
3900
3901 log.debug("writing function starts info from 0x{x} to 0x{x}", .{ offset, offset + needed_size_aligned });
3902
3903 try self.base.file.?.pwriteAll(buffer.items, offset);
3904
3905 self.function_starts_cmd.dataoff = @as(u32, @intCast(offset));
3906 self.function_starts_cmd.datasize = @as(u32, @intCast(needed_size_aligned));
3907}
3908
3909fn filterDataInCode(
3910 dices: []const macho.data_in_code_entry,
3911 start_addr: u64,
3912 end_addr: u64,
3913) []const macho.data_in_code_entry {
3914 const Predicate = struct {
3915 addr: u64,
3916
3917 pub fn predicate(self: @This(), dice: macho.data_in_code_entry) bool {
3918 return dice.offset >= self.addr;
3919 }
3920 };
3921
3922 const start = MachO.lsearch(macho.data_in_code_entry, dices, Predicate{ .addr = start_addr });
3923 const end = MachO.lsearch(macho.data_in_code_entry, dices[start..], Predicate{ .addr = end_addr }) + start;
3924
3925 return dices[start..end];
3926}
3927
3928pub fn writeDataInCode(self: *MachO) !void {
3929 const gpa = self.base.allocator;
3930 var out_dice = std.ArrayList(macho.data_in_code_entry).init(gpa);
3931 defer out_dice.deinit();
3932
3933 const text_sect_id = self.text_section_index orelse return;
3934 const text_sect_header = self.sections.items(.header)[text_sect_id];
3935
3936 for (self.objects.items) |object| {
3937 if (!object.hasDataInCode()) continue;
3938 const dice = object.data_in_code.items;
3939 try out_dice.ensureUnusedCapacity(dice.len);
3940
3941 for (object.exec_atoms.items) |atom_index| {
3942 const atom = self.getAtom(atom_index);
3943 const sym = self.getSymbol(atom.getSymbolWithLoc());
3944 if (sym.n_desc == MachO.N_DEAD) continue;
3945
3946 const source_addr = if (object.getSourceSymbol(atom.sym_index)) |source_sym|
3947 source_sym.n_value
3948 else blk: {
3949 const nbase = @as(u32, @intCast(object.in_symtab.?.len));
3950 const source_sect_id = @as(u8, @intCast(atom.sym_index - nbase));
3951 break :blk object.getSourceSection(source_sect_id).addr;
3952 };
3953 const filtered_dice = filterDataInCode(dice, source_addr, source_addr + atom.size);
3954 const base = math.cast(u32, sym.n_value - text_sect_header.addr + text_sect_header.offset) orelse
3955 return error.Overflow;
3956
3957 for (filtered_dice) |single| {
3958 const offset = math.cast(u32, single.offset - source_addr + base) orelse
3959 return error.Overflow;
3960 out_dice.appendAssumeCapacity(.{
3961 .offset = offset,
3962 .length = single.length,
3963 .kind = single.kind,
3964 });
3965 }
3966 }
3967 }
3968
3969 const seg = self.getLinkeditSegmentPtr();
3970 const offset = seg.fileoff + seg.filesize;
3971 assert(mem.isAlignedGeneric(u64, offset, @alignOf(u64)));
3972 const needed_size = out_dice.items.len * @sizeOf(macho.data_in_code_entry);
3973 const needed_size_aligned = mem.alignForward(u64, needed_size, @alignOf(u64));
3974 seg.filesize = offset + needed_size_aligned - seg.fileoff;
3975
3976 const buffer = try gpa.alloc(u8, math.cast(usize, needed_size_aligned) orelse return error.Overflow);
3977 defer gpa.free(buffer);
3978 {
3979 const src = mem.sliceAsBytes(out_dice.items);
3980 @memcpy(buffer[0..src.len], src);
3981 @memset(buffer[src.len..], 0);
3982 }
3983
3984 log.debug("writing data-in-code from 0x{x} to 0x{x}", .{ offset, offset + needed_size_aligned });
3985
3986 try self.base.file.?.pwriteAll(buffer, offset);
3987
3988 self.data_in_code_cmd.dataoff = @as(u32, @intCast(offset));
3989 self.data_in_code_cmd.datasize = @as(u32, @intCast(needed_size_aligned));
3990}
3991
32573992fn writeSymtabs(self: *MachO) !void {
32583993 var ctx = try self.writeSymtab();
32593994 defer ctx.imports_table.deinit();
......@@ -3261,18 +3996,38 @@ fn writeSymtabs(self: *MachO) !void {
32613996 try self.writeStrtab();
32623997}
32633998
3999fn addLocalToSymtab(self: *MachO, sym_loc: SymbolWithLoc, locals: *std.ArrayList(macho.nlist_64)) !void {
4000 const sym = self.getSymbol(sym_loc);
4001 if (sym.n_strx == 0) return; // no name, skip
4002 if (sym.n_desc == MachO.N_DEAD) return; // garbage-collected, skip
4003 if (sym.ext()) return; // an export lands in its own symtab section, skip
4004 if (self.symbolIsTemp(sym_loc)) return; // local temp symbol, skip
4005 var out_sym = sym;
4006 out_sym.n_strx = try self.strtab.insert(self.base.allocator, self.getSymbolName(sym_loc));
4007 try locals.append(out_sym);
4008}
4009
32644010fn writeSymtab(self: *MachO) !SymtabCtx {
32654011 const gpa = self.base.allocator;
32664012
32674013 var locals = std.ArrayList(macho.nlist_64).init(gpa);
32684014 defer locals.deinit();
32694015
3270 for (self.locals.items, 0..) |sym, sym_id| {
3271 if (sym.n_strx == 0) continue; // no name, skip
3272 const sym_loc = SymbolWithLoc{ .sym_index = @as(u32, @intCast(sym_id)), .file = null };
3273 if (self.symbolIsTemp(sym_loc)) continue; // local temp symbol, skip
3274 if (self.getGlobal(self.getSymbolName(sym_loc)) != null) continue; // global symbol is either an export or import, skip
3275 try locals.append(sym);
4016 for (0..self.locals.items.len) |sym_id| {
4017 try self.addLocalToSymtab(.{ .sym_index = @intCast(sym_id) }, &locals);
4018 }
4019
4020 for (self.objects.items) |object| {
4021 for (object.atoms.items) |atom_index| {
4022 const atom = self.getAtom(atom_index);
4023 const sym_loc = atom.getSymbolWithLoc();
4024 try self.addLocalToSymtab(sym_loc, &locals);
4025
4026 var it = Atom.getInnerSymbolsIterator(self, atom_index);
4027 while (it.next()) |inner_sym_loc| {
4028 try self.addLocalToSymtab(inner_sym_loc, &locals);
4029 }
4030 }
32764031 }
32774032
32784033 var exports = std.ArrayList(macho.nlist_64).init(gpa);
......@@ -3281,6 +4036,7 @@ fn writeSymtab(self: *MachO) !SymtabCtx {
32814036 for (self.globals.items) |global| {
32824037 const sym = self.getSymbol(global);
32834038 if (sym.undf()) continue; // import, skip
4039 if (sym.n_desc == N_DEAD) continue;
32844040 var out_sym = sym;
32854041 out_sym.n_strx = try self.strtab.insert(gpa, self.getSymbolName(global));
32864042 try exports.append(out_sym);
......@@ -3295,6 +4051,7 @@ fn writeSymtab(self: *MachO) !SymtabCtx {
32954051 const sym = self.getSymbol(global);
32964052 if (sym.n_strx == 0) continue; // no name, skip
32974053 if (!sym.undf()) continue; // not an import, skip
4054 if (sym.n_desc == N_DEAD) continue;
32984055 const new_index = @as(u32, @intCast(imports.items.len));
32994056 var out_sym = sym;
33004057 out_sym.n_strx = try self.strtab.insert(gpa, self.getSymbolName(global));
......@@ -3302,6 +4059,15 @@ fn writeSymtab(self: *MachO) !SymtabCtx {
33024059 try imports_table.putNoClobber(global, new_index);
33034060 }
33044061
4062 // We generate stabs last in order to ensure that the strtab always has debug info
4063 // strings trailing
4064 if (!self.base.options.strip) {
4065 for (self.objects.items) |object| {
4066 assert(self.d_sym == null); // TODO
4067 try self.generateSymbolStabs(object, &locals);
4068 }
4069 }
4070
33054071 const nlocals = @as(u32, @intCast(locals.items.len));
33064072 const nexports = @as(u32, @intCast(exports.items.len));
33074073 const nimports = @as(u32, @intCast(imports.items.len));
......@@ -3335,74 +4101,297 @@ fn writeSymtab(self: *MachO) !SymtabCtx {
33354101 };
33364102}
33374103
3338fn writeStrtab(self: *MachO) !void {
4104// TODO this function currently skips generating symbol stabs in case errors are encountered in DWARF data.
4105// I think we should actually report those errors to the user and let them decide if they want to strip debug info
4106// in that case or not.
4107fn generateSymbolStabs(
4108 self: *MachO,
4109 object: Object,
4110 locals: *std.ArrayList(macho.nlist_64),
4111) !void {
4112 log.debug("generating stabs for '{s}'", .{object.name});
4113
33394114 const gpa = self.base.allocator;
3340 const seg = self.getLinkeditSegmentPtr();
3341 const offset = seg.fileoff + seg.filesize;
3342 assert(mem.isAlignedGeneric(u64, offset, @alignOf(u64)));
3343 const needed_size = self.strtab.buffer.items.len;
3344 const needed_size_aligned = mem.alignForward(u64, needed_size, @alignOf(u64));
3345 seg.filesize = offset + needed_size_aligned - seg.fileoff;
4115 var debug_info = object.parseDwarfInfo();
4116
4117 var lookup = DwarfInfo.AbbrevLookupTable.init(gpa);
4118 defer lookup.deinit();
4119 try lookup.ensureUnusedCapacity(std.math.maxInt(u8));
4120
4121 // We assume there is only one CU.
4122 var cu_it = debug_info.getCompileUnitIterator();
4123 const compile_unit = while (try cu_it.next()) |cu| {
4124 const offset = math.cast(usize, cu.cuh.debug_abbrev_offset) orelse return error.Overflow;
4125 try debug_info.genAbbrevLookupByKind(offset, &lookup);
4126 break cu;
4127 } else {
4128 log.debug("no compile unit found in debug info in {s}; skipping", .{object.name});
4129 return;
4130 };
33464131
3347 log.debug("writing string table from 0x{x} to 0x{x}", .{ offset, offset + needed_size_aligned });
4132 var abbrev_it = compile_unit.getAbbrevEntryIterator(debug_info);
4133 const maybe_cu_entry: ?DwarfInfo.AbbrevEntry = blk: {
4134 while (abbrev_it.next(lookup) catch break :blk null) |entry| switch (entry.tag) {
4135 dwarf.TAG.compile_unit => break :blk entry,
4136 else => continue,
4137 } else break :blk null;
4138 };
33484139
3349 const buffer = try gpa.alloc(u8, math.cast(usize, needed_size_aligned) orelse return error.Overflow);
3350 defer gpa.free(buffer);
3351 @memcpy(buffer[0..self.strtab.buffer.items.len], self.strtab.buffer.items);
3352 @memset(buffer[self.strtab.buffer.items.len..], 0);
4140 const cu_entry = maybe_cu_entry orelse {
4141 log.debug("missing DWARF_TAG_compile_unit tag in {s}; skipping", .{object.name});
4142 return;
4143 };
33534144
3354 try self.base.file.?.pwriteAll(buffer, offset);
4145 var maybe_tu_name: ?[]const u8 = null;
4146 var maybe_tu_comp_dir: ?[]const u8 = null;
4147 var attr_it = cu_entry.getAttributeIterator(debug_info, compile_unit.cuh);
33554148
3356 self.symtab_cmd.stroff = @as(u32, @intCast(offset));
3357 self.symtab_cmd.strsize = @as(u32, @intCast(needed_size_aligned));
3358}
4149 blk: {
4150 while (attr_it.next() catch break :blk) |attr| switch (attr.name) {
4151 dwarf.AT.comp_dir => maybe_tu_comp_dir = attr.getString(debug_info, compile_unit.cuh) orelse continue,
4152 dwarf.AT.name => maybe_tu_name = attr.getString(debug_info, compile_unit.cuh) orelse continue,
4153 else => continue,
4154 };
4155 }
33594156
3360const SymtabCtx = struct {
3361 nlocalsym: u32,
3362 nextdefsym: u32,
3363 nundefsym: u32,
3364 imports_table: std.AutoHashMap(SymbolWithLoc, u32),
3365};
4157 if (maybe_tu_name == null or maybe_tu_comp_dir == null) {
4158 log.debug("missing DWARF_AT_comp_dir and DWARF_AT_name attributes {s}; skipping", .{object.name});
4159 return;
4160 }
33664161
3367fn writeDysymtab(self: *MachO, ctx: SymtabCtx) !void {
3368 const gpa = self.base.allocator;
3369 const nstubs = @as(u32, @intCast(self.stub_table.lookup.count()));
3370 const ngot_entries = @as(u32, @intCast(self.got_table.lookup.count()));
3371 const nindirectsyms = nstubs * 2 + ngot_entries;
3372 const iextdefsym = ctx.nlocalsym;
3373 const iundefsym = iextdefsym + ctx.nextdefsym;
4162 const tu_name = maybe_tu_name.?;
4163 const tu_comp_dir = maybe_tu_comp_dir.?;
33744164
3375 const seg = self.getLinkeditSegmentPtr();
3376 const offset = seg.fileoff + seg.filesize;
3377 assert(mem.isAlignedGeneric(u64, offset, @alignOf(u64)));
3378 const needed_size = nindirectsyms * @sizeOf(u32);
3379 const needed_size_aligned = mem.alignForward(u64, needed_size, @alignOf(u64));
3380 seg.filesize = offset + needed_size_aligned - seg.fileoff;
4165 // Open scope
4166 try locals.ensureUnusedCapacity(3);
4167 locals.appendAssumeCapacity(.{
4168 .n_strx = try self.strtab.insert(gpa, tu_comp_dir),
4169 .n_type = macho.N_SO,
4170 .n_sect = 0,
4171 .n_desc = 0,
4172 .n_value = 0,
4173 });
4174 locals.appendAssumeCapacity(.{
4175 .n_strx = try self.strtab.insert(gpa, tu_name),
4176 .n_type = macho.N_SO,
4177 .n_sect = 0,
4178 .n_desc = 0,
4179 .n_value = 0,
4180 });
4181 locals.appendAssumeCapacity(.{
4182 .n_strx = try self.strtab.insert(gpa, object.name),
4183 .n_type = macho.N_OSO,
4184 .n_sect = 0,
4185 .n_desc = 1,
4186 .n_value = object.mtime,
4187 });
33814188
3382 log.debug("writing indirect symbol table from 0x{x} to 0x{x}", .{ offset, offset + needed_size_aligned });
4189 var stabs_buf: [4]macho.nlist_64 = undefined;
33834190
3384 var buf = std.ArrayList(u8).init(gpa);
3385 defer buf.deinit();
3386 try buf.ensureTotalCapacity(math.cast(usize, needed_size_aligned) orelse return error.Overflow);
3387 const writer = buf.writer();
4191 var name_lookup: ?DwarfInfo.SubprogramLookupByName = if (object.header.flags & macho.MH_SUBSECTIONS_VIA_SYMBOLS == 0) blk: {
4192 var name_lookup = DwarfInfo.SubprogramLookupByName.init(gpa);
4193 errdefer name_lookup.deinit();
4194 try name_lookup.ensureUnusedCapacity(@as(u32, @intCast(object.atoms.items.len)));
4195 debug_info.genSubprogramLookupByName(compile_unit, lookup, &name_lookup) catch |err| switch (err) {
4196 error.UnhandledDwFormValue => {}, // TODO I don't like the fact we constantly re-iterate and hit this; we should validate once a priori
4197 else => |e| return e,
4198 };
4199 break :blk name_lookup;
4200 } else null;
4201 defer if (name_lookup) |*nl| nl.deinit();
33884202
3389 if (self.stubs_section_index) |sect_id| {
3390 const stubs_header = &self.sections.items(.header)[sect_id];
3391 stubs_header.reserved1 = 0;
3392 for (self.stub_table.entries.items) |entry| {
3393 if (!self.stub_table.lookup.contains(entry)) continue;
3394 const target_sym = self.getSymbol(entry);
3395 assert(target_sym.undf());
3396 try writer.writeIntLittle(u32, iundefsym + ctx.imports_table.get(entry).?);
4203 for (object.atoms.items) |atom_index| {
4204 const atom = self.getAtom(atom_index);
4205 const stabs = try self.generateSymbolStabsForSymbol(
4206 atom_index,
4207 atom.getSymbolWithLoc(),
4208 name_lookup,
4209 &stabs_buf,
4210 );
4211 try locals.appendSlice(stabs);
4212
4213 var it = Atom.getInnerSymbolsIterator(self, atom_index);
4214 while (it.next()) |sym_loc| {
4215 const contained_stabs = try self.generateSymbolStabsForSymbol(
4216 atom_index,
4217 sym_loc,
4218 name_lookup,
4219 &stabs_buf,
4220 );
4221 try locals.appendSlice(contained_stabs);
33974222 }
33984223 }
33994224
3400 if (self.got_section_index) |sect_id| {
3401 const got = &self.sections.items(.header)[sect_id];
3402 got.reserved1 = nstubs;
3403 for (self.got_table.entries.items) |entry| {
3404 if (!self.got_table.lookup.contains(entry)) continue;
3405 const target_sym = self.getSymbol(entry);
4225 // Close scope
4226 try locals.append(.{
4227 .n_strx = 0,
4228 .n_type = macho.N_SO,
4229 .n_sect = 0,
4230 .n_desc = 0,
4231 .n_value = 0,
4232 });
4233}
4234
4235fn generateSymbolStabsForSymbol(
4236 self: *MachO,
4237 atom_index: Atom.Index,
4238 sym_loc: SymbolWithLoc,
4239 lookup: ?DwarfInfo.SubprogramLookupByName,
4240 buf: *[4]macho.nlist_64,
4241) ![]const macho.nlist_64 {
4242 const gpa = self.base.allocator;
4243 const object = self.objects.items[sym_loc.getFile().?];
4244 const sym = self.getSymbol(sym_loc);
4245 const sym_name = self.getSymbolName(sym_loc);
4246 const header = self.sections.items(.header)[sym.n_sect - 1];
4247
4248 if (sym.n_strx == 0) return buf[0..0];
4249 if (self.symbolIsTemp(sym_loc)) return buf[0..0];
4250
4251 if (!header.isCode()) {
4252 // Since we are not dealing with machine code, it's either a global or a static depending
4253 // on the linkage scope.
4254 if (sym.sect() and sym.ext()) {
4255 // Global gets an N_GSYM stab type.
4256 buf[0] = .{
4257 .n_strx = try self.strtab.insert(gpa, sym_name),
4258 .n_type = macho.N_GSYM,
4259 .n_sect = sym.n_sect,
4260 .n_desc = 0,
4261 .n_value = 0,
4262 };
4263 } else {
4264 // Local static gets an N_STSYM stab type.
4265 buf[0] = .{
4266 .n_strx = try self.strtab.insert(gpa, sym_name),
4267 .n_type = macho.N_STSYM,
4268 .n_sect = sym.n_sect,
4269 .n_desc = 0,
4270 .n_value = sym.n_value,
4271 };
4272 }
4273 return buf[0..1];
4274 }
4275
4276 const size: u64 = size: {
4277 if (object.header.flags & macho.MH_SUBSECTIONS_VIA_SYMBOLS != 0) {
4278 break :size self.getAtom(atom_index).size;
4279 }
4280
4281 // Since we don't have subsections to work with, we need to infer the size of each function
4282 // the slow way by scanning the debug info for matching symbol names and extracting
4283 // the symbol's DWARF_AT_low_pc and DWARF_AT_high_pc values.
4284 const source_sym = object.getSourceSymbol(sym_loc.sym_index) orelse return buf[0..0];
4285 const subprogram = lookup.?.get(sym_name[1..]) orelse return buf[0..0];
4286
4287 if (subprogram.addr <= source_sym.n_value and source_sym.n_value < subprogram.addr + subprogram.size) {
4288 break :size subprogram.size;
4289 } else {
4290 log.debug("no stab found for {s}", .{sym_name});
4291 return buf[0..0];
4292 }
4293 };
4294
4295 buf[0] = .{
4296 .n_strx = 0,
4297 .n_type = macho.N_BNSYM,
4298 .n_sect = sym.n_sect,
4299 .n_desc = 0,
4300 .n_value = sym.n_value,
4301 };
4302 buf[1] = .{
4303 .n_strx = try self.strtab.insert(gpa, sym_name),
4304 .n_type = macho.N_FUN,
4305 .n_sect = sym.n_sect,
4306 .n_desc = 0,
4307 .n_value = sym.n_value,
4308 };
4309 buf[2] = .{
4310 .n_strx = 0,
4311 .n_type = macho.N_FUN,
4312 .n_sect = 0,
4313 .n_desc = 0,
4314 .n_value = size,
4315 };
4316 buf[3] = .{
4317 .n_strx = 0,
4318 .n_type = macho.N_ENSYM,
4319 .n_sect = sym.n_sect,
4320 .n_desc = 0,
4321 .n_value = size,
4322 };
4323
4324 return buf;
4325}
4326
4327pub fn writeStrtab(self: *MachO) !void {
4328 const gpa = self.base.allocator;
4329 const seg = self.getLinkeditSegmentPtr();
4330 const offset = seg.fileoff + seg.filesize;
4331 assert(mem.isAlignedGeneric(u64, offset, @alignOf(u64)));
4332 const needed_size = self.strtab.buffer.items.len;
4333 const needed_size_aligned = mem.alignForward(u64, needed_size, @alignOf(u64));
4334 seg.filesize = offset + needed_size_aligned - seg.fileoff;
4335
4336 log.debug("writing string table from 0x{x} to 0x{x}", .{ offset, offset + needed_size_aligned });
4337
4338 const buffer = try gpa.alloc(u8, math.cast(usize, needed_size_aligned) orelse return error.Overflow);
4339 defer gpa.free(buffer);
4340 @memcpy(buffer[0..self.strtab.buffer.items.len], self.strtab.buffer.items);
4341 @memset(buffer[self.strtab.buffer.items.len..], 0);
4342
4343 try self.base.file.?.pwriteAll(buffer, offset);
4344
4345 self.symtab_cmd.stroff = @as(u32, @intCast(offset));
4346 self.symtab_cmd.strsize = @as(u32, @intCast(needed_size_aligned));
4347}
4348
4349const SymtabCtx = struct {
4350 nlocalsym: u32,
4351 nextdefsym: u32,
4352 nundefsym: u32,
4353 imports_table: std.AutoHashMap(SymbolWithLoc, u32),
4354};
4355
4356pub fn writeDysymtab(self: *MachO, ctx: SymtabCtx) !void {
4357 const gpa = self.base.allocator;
4358 const nstubs = @as(u32, @intCast(self.stub_table.lookup.count()));
4359 const ngot_entries = @as(u32, @intCast(self.got_table.lookup.count()));
4360 const nindirectsyms = nstubs * 2 + ngot_entries;
4361 const iextdefsym = ctx.nlocalsym;
4362 const iundefsym = iextdefsym + ctx.nextdefsym;
4363
4364 const seg = self.getLinkeditSegmentPtr();
4365 const offset = seg.fileoff + seg.filesize;
4366 assert(mem.isAlignedGeneric(u64, offset, @alignOf(u64)));
4367 const needed_size = nindirectsyms * @sizeOf(u32);
4368 const needed_size_aligned = mem.alignForward(u64, needed_size, @alignOf(u64));
4369 seg.filesize = offset + needed_size_aligned - seg.fileoff;
4370
4371 log.debug("writing indirect symbol table from 0x{x} to 0x{x}", .{ offset, offset + needed_size_aligned });
4372
4373 var buf = std.ArrayList(u8).init(gpa);
4374 defer buf.deinit();
4375 try buf.ensureTotalCapacity(math.cast(usize, needed_size_aligned) orelse return error.Overflow);
4376 const writer = buf.writer();
4377
4378 if (self.stubs_section_index) |sect_id| {
4379 const stubs_header = &self.sections.items(.header)[sect_id];
4380 stubs_header.reserved1 = 0;
4381 for (self.stub_table.entries.items) |entry| {
4382 if (!self.stub_table.lookup.contains(entry)) continue;
4383 const target_sym = self.getSymbol(entry);
4384 assert(target_sym.undf());
4385 try writer.writeIntLittle(u32, iundefsym + ctx.imports_table.get(entry).?);
4386 }
4387 }
4388
4389 if (self.got_section_index) |sect_id| {
4390 const got = &self.sections.items(.header)[sect_id];
4391 got.reserved1 = nstubs;
4392 for (self.got_table.entries.items) |entry| {
4393 if (!self.got_table.lookup.contains(entry)) continue;
4394 const target_sym = self.getSymbol(entry);
34064395 if (target_sym.undf()) {
34074396 try writer.writeIntLittle(u32, iundefsym + ctx.imports_table.get(entry).?);
34084397 } else {
......@@ -3439,7 +4428,7 @@ fn writeDysymtab(self: *MachO, ctx: SymtabCtx) !void {
34394428 self.dysymtab_cmd.nindirectsyms = nindirectsyms;
34404429}
34414430
3442fn writeUuid(self: *MachO, comp: *const Compilation, uuid_cmd_offset: u32, has_codesig: bool) !void {
4431pub fn writeUuid(self: *MachO, comp: *const Compilation, uuid_cmd_offset: u32, has_codesig: bool) !void {
34434432 const file_size = if (!has_codesig) blk: {
34444433 const seg = self.getLinkeditSegmentPtr();
34454434 break :blk seg.fileoff + seg.filesize;
......@@ -3449,14 +4438,14 @@ fn writeUuid(self: *MachO, comp: *const Compilation, uuid_cmd_offset: u32, has_c
34494438 try self.base.file.?.pwriteAll(&self.uuid_cmd.uuid, offset);
34504439}
34514440
3452fn writeCodeSignaturePadding(self: *MachO, code_sig: *CodeSignature) !void {
4441pub fn writeCodeSignaturePadding(self: *MachO, code_sig: *CodeSignature) !void {
34534442 const seg = self.getLinkeditSegmentPtr();
34544443 // Code signature data has to be 16-bytes aligned for Apple tools to recognize the file
34554444 // https://github.com/opensource-apple/cctools/blob/fdb4825f303fd5c0751be524babd32958181b3ed/libstuff/checkout.c#L271
34564445 const offset = mem.alignForward(u64, seg.fileoff + seg.filesize, 16);
34574446 const needed_size = code_sig.estimateSize(offset);
34584447 seg.filesize = offset + needed_size - seg.fileoff;
3459 seg.vmsize = mem.alignForward(u64, seg.filesize, self.page_size);
4448 seg.vmsize = mem.alignForward(u64, seg.filesize, getPageSize(self.base.options.target.cpu.arch));
34604449 log.debug("writing code signature padding from 0x{x} to 0x{x}", .{ offset, offset + needed_size });
34614450 // Pad out the space. We need to do this to calculate valid hashes for everything in the file
34624451 // except for code signature data.
......@@ -3466,8 +4455,9 @@ fn writeCodeSignaturePadding(self: *MachO, code_sig: *CodeSignature) !void {
34664455 self.codesig_cmd.datasize = @as(u32, @intCast(needed_size));
34674456}
34684457
3469fn writeCodeSignature(self: *MachO, comp: *const Compilation, code_sig: *CodeSignature) !void {
3470 const seg = self.getSegment(self.text_section_index.?);
4458pub fn writeCodeSignature(self: *MachO, comp: *const Compilation, code_sig: *CodeSignature) !void {
4459 const seg_id = self.header_segment_cmd_index.?;
4460 const seg = self.segments.items[seg_id];
34714461 const offset = self.codesig_cmd.dataoff;
34724462
34734463 var buffer = std.ArrayList(u8).init(self.base.allocator);
......@@ -3491,14 +4481,10 @@ fn writeCodeSignature(self: *MachO, comp: *const Compilation, code_sig: *CodeSig
34914481}
34924482
34934483/// Writes Mach-O file header.
3494fn writeHeader(self: *MachO, ncmds: u32, sizeofcmds: u32) !void {
4484pub fn writeHeader(self: *MachO, ncmds: u32, sizeofcmds: u32) !void {
34954485 var header: macho.mach_header_64 = .{};
34964486 header.flags = macho.MH_NOUNDEFS | macho.MH_DYLDLINK | macho.MH_PIE | macho.MH_TWOLEVEL;
34974487
3498 if (!self.base.options.single_threaded) {
3499 header.flags |= macho.MH_HAS_TLV_DESCRIPTORS;
3500 }
3501
35024488 switch (self.base.options.target.cpu.arch) {
35034489 .aarch64 => {
35044490 header.cputype = macho.CPU_TYPE_ARM64;
......@@ -3508,7 +4494,7 @@ fn writeHeader(self: *MachO, ncmds: u32, sizeofcmds: u32) !void {
35084494 header.cputype = macho.CPU_TYPE_X86_64;
35094495 header.cpusubtype = macho.CPU_SUBTYPE_X86_64_ALL;
35104496 },
3511 else => return error.UnsupportedCpuArchitecture,
4497 else => unreachable,
35124498 }
35134499
35144500 switch (self.base.options.output_mode) {
......@@ -3523,6 +4509,13 @@ fn writeHeader(self: *MachO, ncmds: u32, sizeofcmds: u32) !void {
35234509 else => unreachable,
35244510 }
35254511
4512 if (self.thread_vars_section_index) |sect_id| {
4513 header.flags |= macho.MH_HAS_TLV_DESCRIPTORS;
4514 if (self.sections.items(.header)[sect_id].size > 0) {
4515 header.flags |= macho.MH_HAS_TLV_DESCRIPTORS;
4516 }
4517 }
4518
35264519 header.ncmds = ncmds;
35274520 header.sizeofcmds = sizeofcmds;
35284521
......@@ -3618,9 +4611,7 @@ fn addUndefined(self: *MachO, name: []const u8, action: ResolveAction.Kind) !u32
36184611 const gop = try self.getOrPutGlobalPtr(name);
36194612 const global_index = self.getGlobalIndex(name).?;
36204613
3621 if (gop.found_existing) {
3622 return global_index;
3623 }
4614 if (gop.found_existing) return global_index;
36244615
36254616 const sym_index = try self.allocateSymbol();
36264617 const sym_loc = SymbolWithLoc{ .sym_index = sym_index };
......@@ -3641,7 +4632,7 @@ pub fn makeStaticString(bytes: []const u8) [16]u8 {
36414632 return buf;
36424633}
36434634
3644fn getSegmentByName(self: MachO, segname: []const u8) ?u8 {
4635pub fn getSegmentByName(self: MachO, segname: []const u8) ?u8 {
36454636 for (self.segments.items, 0..) |seg, i| {
36464637 if (mem.eql(u8, segname, seg.segName())) return @as(u8, @intCast(i));
36474638 } else return null;
......@@ -3689,20 +4680,33 @@ pub fn symbolIsTemp(self: *MachO, sym_with_loc: SymbolWithLoc) bool {
36894680
36904681/// Returns pointer-to-symbol described by `sym_with_loc` descriptor.
36914682pub fn getSymbolPtr(self: *MachO, sym_with_loc: SymbolWithLoc) *macho.nlist_64 {
3692 assert(sym_with_loc.file == null);
3693 return &self.locals.items[sym_with_loc.sym_index];
4683 if (sym_with_loc.getFile()) |file| {
4684 const object = &self.objects.items[file];
4685 return &object.symtab[sym_with_loc.sym_index];
4686 } else {
4687 return &self.locals.items[sym_with_loc.sym_index];
4688 }
36944689}
36954690
36964691/// Returns symbol described by `sym_with_loc` descriptor.
36974692pub fn getSymbol(self: *const MachO, sym_with_loc: SymbolWithLoc) macho.nlist_64 {
3698 assert(sym_with_loc.file == null);
3699 return self.locals.items[sym_with_loc.sym_index];
4693 if (sym_with_loc.getFile()) |file| {
4694 const object = &self.objects.items[file];
4695 return object.symtab[sym_with_loc.sym_index];
4696 } else {
4697 return self.locals.items[sym_with_loc.sym_index];
4698 }
37004699}
37014700
37024701/// Returns name of the symbol described by `sym_with_loc` descriptor.
37034702pub fn getSymbolName(self: *const MachO, sym_with_loc: SymbolWithLoc) []const u8 {
3704 const sym = self.getSymbol(sym_with_loc);
3705 return self.strtab.get(sym.n_strx).?;
4703 if (sym_with_loc.getFile()) |file| {
4704 const object = self.objects.items[file];
4705 return object.getSymbolName(sym_with_loc.sym_index);
4706 } else {
4707 const sym = self.locals.items[sym_with_loc.sym_index];
4708 return self.strtab.get(sym.n_strx).?;
4709 }
37064710}
37074711
37084712/// Returns pointer to the global entry for `name` if one exists.
......@@ -3761,18 +4765,33 @@ pub fn getAtomPtr(self: *MachO, atom_index: Atom.Index) *Atom {
37614765/// Returns atom if there is an atom referenced by the symbol described by `sym_with_loc` descriptor.
37624766/// Returns null on failure.
37634767pub fn getAtomIndexForSymbol(self: *MachO, sym_with_loc: SymbolWithLoc) ?Atom.Index {
3764 assert(sym_with_loc.file == null);
4768 assert(sym_with_loc.getFile() == null);
37654769 return self.atom_by_index_table.get(sym_with_loc.sym_index);
37664770}
37674771
3768/// Returns symbol location corresponding to the set entrypoint.
4772pub fn getGotEntryAddress(self: *MachO, sym_with_loc: SymbolWithLoc) ?u64 {
4773 const index = self.got_table.lookup.get(sym_with_loc) orelse return null;
4774 const header = self.sections.items(.header)[self.got_section_index.?];
4775 return header.addr + @sizeOf(u64) * index;
4776}
4777
4778pub fn getTlvPtrEntryAddress(self: *MachO, sym_with_loc: SymbolWithLoc) ?u64 {
4779 const index = self.tlv_ptr_table.lookup.get(sym_with_loc) orelse return null;
4780 const header = self.sections.items(.header)[self.tlv_ptr_section_index.?];
4781 return header.addr + @sizeOf(u64) * index;
4782}
4783
4784pub fn getStubsEntryAddress(self: *MachO, sym_with_loc: SymbolWithLoc) ?u64 {
4785 const index = self.stub_table.lookup.get(sym_with_loc) orelse return null;
4786 const header = self.sections.items(.header)[self.stubs_section_index.?];
4787 return header.addr + stubs.stubSize(self.base.options.target.cpu.arch) * index;
4788}
4789
4790/// Returns symbol location corresponding to the set entrypoint if any.
37694791/// Asserts output mode is executable.
3770pub fn getEntryPoint(self: MachO) error{MissingMainEntrypoint}!SymbolWithLoc {
4792pub fn getEntryPoint(self: MachO) ?SymbolWithLoc {
37714793 const entry_name = self.base.options.entry orelse load_commands.default_entry_point;
3772 const global = self.getGlobal(entry_name) orelse {
3773 log.err("entrypoint '{s}' not found", .{entry_name});
3774 return error.MissingMainEntrypoint;
3775 };
4794 const global = self.getGlobal(entry_name) orelse return null;
37764795 return global;
37774796}
37784797
......@@ -3781,273 +4800,318 @@ pub fn getDebugSymbols(self: *MachO) ?*DebugSymbols {
37814800 return &self.d_sym.?;
37824801}
37834802
3784pub fn findFirst(comptime T: type, haystack: []align(1) const T, start: usize, predicate: anytype) usize {
4803pub inline fn getPageSize(cpu_arch: std.Target.Cpu.Arch) u16 {
4804 return switch (cpu_arch) {
4805 .aarch64 => 0x4000,
4806 .x86_64 => 0x1000,
4807 else => unreachable,
4808 };
4809}
4810
4811pub fn requiresCodeSignature(options: *const link.Options) bool {
4812 if (options.entitlements) |_| return true;
4813 const cpu_arch = options.target.cpu.arch;
4814 const os_tag = options.target.os.tag;
4815 const abi = options.target.abi;
4816 if (cpu_arch == .aarch64 and (os_tag == .macos or abi == .simulator)) return true;
4817 return false;
4818}
4819
4820pub fn getSegmentPrecedence(segname: []const u8) u4 {
4821 if (mem.eql(u8, segname, "__PAGEZERO")) return 0x0;
4822 if (mem.eql(u8, segname, "__TEXT")) return 0x1;
4823 if (mem.eql(u8, segname, "__DATA_CONST")) return 0x2;
4824 if (mem.eql(u8, segname, "__DATA")) return 0x3;
4825 if (mem.eql(u8, segname, "__LINKEDIT")) return 0x5;
4826 return 0x4;
4827}
4828
4829pub fn getSegmentMemoryProtection(segname: []const u8) macho.vm_prot_t {
4830 if (mem.eql(u8, segname, "__PAGEZERO")) return macho.PROT.NONE;
4831 if (mem.eql(u8, segname, "__TEXT")) return macho.PROT.READ | macho.PROT.EXEC;
4832 if (mem.eql(u8, segname, "__LINKEDIT")) return macho.PROT.READ;
4833 return macho.PROT.READ | macho.PROT.WRITE;
4834}
4835
4836pub fn getSectionPrecedence(header: macho.section_64) u8 {
4837 const segment_precedence: u4 = getSegmentPrecedence(header.segName());
4838 const section_precedence: u4 = blk: {
4839 if (header.isCode()) {
4840 if (mem.eql(u8, "__text", header.sectName())) break :blk 0x0;
4841 if (header.type() == macho.S_SYMBOL_STUBS) break :blk 0x1;
4842 break :blk 0x2;
4843 }
4844 switch (header.type()) {
4845 macho.S_NON_LAZY_SYMBOL_POINTERS,
4846 macho.S_LAZY_SYMBOL_POINTERS,
4847 => break :blk 0x0,
4848 macho.S_MOD_INIT_FUNC_POINTERS => break :blk 0x1,
4849 macho.S_MOD_TERM_FUNC_POINTERS => break :blk 0x2,
4850 macho.S_ZEROFILL => break :blk 0xf,
4851 macho.S_THREAD_LOCAL_REGULAR => break :blk 0xd,
4852 macho.S_THREAD_LOCAL_ZEROFILL => break :blk 0xe,
4853 else => {
4854 if (mem.eql(u8, "__unwind_info", header.sectName())) break :blk 0xe;
4855 if (mem.eql(u8, "__eh_frame", header.sectName())) break :blk 0xf;
4856 break :blk 0x3;
4857 },
4858 }
4859 };
4860 return (@as(u8, @intCast(segment_precedence)) << 4) + section_precedence;
4861}
4862
4863pub const ParseErrorCtx = struct {
4864 arena_allocator: std.heap.ArenaAllocator,
4865 detected_dylib_id: struct {
4866 parent: u16,
4867 required_version: u32,
4868 found_version: u32,
4869 },
4870 detected_targets: std.ArrayList([]const u8),
4871
4872 pub fn init(gpa: Allocator) ParseErrorCtx {
4873 return .{
4874 .arena_allocator = std.heap.ArenaAllocator.init(gpa),
4875 .detected_dylib_id = undefined,
4876 .detected_targets = std.ArrayList([]const u8).init(gpa),
4877 };
4878 }
4879
4880 pub fn deinit(ctx: *ParseErrorCtx) void {
4881 ctx.arena_allocator.deinit();
4882 ctx.detected_targets.deinit();
4883 }
4884
4885 pub fn arena(ctx: *ParseErrorCtx) Allocator {
4886 return ctx.arena_allocator.allocator();
4887 }
4888};
4889
4890pub fn handleAndReportParseError(
4891 self: *MachO,
4892 path: []const u8,
4893 err: ParseError,
4894 ctx: *const ParseErrorCtx,
4895) error{OutOfMemory}!void {
4896 const cpu_arch = self.base.options.target.cpu.arch;
4897 switch (err) {
4898 error.DylibAlreadyExists => {},
4899 error.IncompatibleDylibVersion => {
4900 const parent = &self.dylibs.items[ctx.detected_dylib_id.parent];
4901 try self.reportDependencyError(
4902 if (parent.id) |id| id.name else parent.path,
4903 path,
4904 "incompatible dylib version: expected at least '{}', but found '{}'",
4905 .{
4906 load_commands.appleVersionToSemanticVersion(ctx.detected_dylib_id.required_version),
4907 load_commands.appleVersionToSemanticVersion(ctx.detected_dylib_id.found_version),
4908 },
4909 );
4910 },
4911 error.UnknownFileType => try self.reportParseError(path, "unknown file type", .{}),
4912 error.InvalidTarget, error.InvalidTargetFatLibrary => {
4913 var targets_string = std.ArrayList(u8).init(self.base.allocator);
4914 defer targets_string.deinit();
4915
4916 if (ctx.detected_targets.items.len > 1) {
4917 try targets_string.writer().writeAll("(");
4918 for (ctx.detected_targets.items) |t| {
4919 try targets_string.writer().print("{s}, ", .{t});
4920 }
4921 try targets_string.resize(targets_string.items.len - 2);
4922 try targets_string.writer().writeAll(")");
4923 } else {
4924 try targets_string.writer().writeAll(ctx.detected_targets.items[0]);
4925 }
4926
4927 switch (err) {
4928 error.InvalidTarget => try self.reportParseError(
4929 path,
4930 "invalid target: expected '{}', but found '{s}'",
4931 .{ Platform.fromTarget(self.base.options.target).fmtTarget(cpu_arch), targets_string.items },
4932 ),
4933 error.InvalidTargetFatLibrary => try self.reportParseError(
4934 path,
4935 "invalid architecture in universal library: expected '{s}', but found '{s}'",
4936 .{ @tagName(cpu_arch), targets_string.items },
4937 ),
4938 else => unreachable,
4939 }
4940 },
4941 else => |e| try self.reportParseError(path, "{s}: parsing object failed", .{@errorName(e)}),
4942 }
4943}
4944
4945fn reportDependencyError(
4946 self: *MachO,
4947 parent: []const u8,
4948 path: ?[]const u8,
4949 comptime format: []const u8,
4950 args: anytype,
4951) error{OutOfMemory}!void {
4952 const gpa = self.base.allocator;
4953 try self.misc_errors.ensureUnusedCapacity(gpa, 1);
4954 var notes = try std.ArrayList(File.ErrorMsg).initCapacity(gpa, 2);
4955 defer notes.deinit();
4956 if (path) |p| {
4957 notes.appendAssumeCapacity(.{ .msg = try std.fmt.allocPrint(gpa, "while parsing {s}", .{p}) });
4958 }
4959 notes.appendAssumeCapacity(.{ .msg = try std.fmt.allocPrint(gpa, "a dependency of {s}", .{parent}) });
4960 self.misc_errors.appendAssumeCapacity(.{
4961 .msg = try std.fmt.allocPrint(gpa, format, args),
4962 .notes = try notes.toOwnedSlice(),
4963 });
4964}
4965
4966pub fn reportParseError(
4967 self: *MachO,
4968 path: []const u8,
4969 comptime format: []const u8,
4970 args: anytype,
4971) error{OutOfMemory}!void {
4972 const gpa = self.base.allocator;
4973 try self.misc_errors.ensureUnusedCapacity(gpa, 1);
4974 var notes = try gpa.alloc(File.ErrorMsg, 1);
4975 errdefer gpa.free(notes);
4976 notes[0] = .{ .msg = try std.fmt.allocPrint(gpa, "while parsing {s}", .{path}) };
4977 self.misc_errors.appendAssumeCapacity(.{
4978 .msg = try std.fmt.allocPrint(gpa, format, args),
4979 .notes = notes,
4980 });
4981}
4982
4983pub fn reportUndefined(self: *MachO) error{OutOfMemory}!void {
4984 const gpa = self.base.allocator;
4985 const count = self.unresolved.count();
4986 try self.misc_errors.ensureUnusedCapacity(gpa, count);
4987
4988 for (self.unresolved.keys()) |global_index| {
4989 const global = self.globals.items[global_index];
4990 const sym_name = self.getSymbolName(global);
4991
4992 var notes = try std.ArrayList(File.ErrorMsg).initCapacity(gpa, 1);
4993 defer notes.deinit();
4994
4995 if (global.getFile()) |file| {
4996 const note = try std.fmt.allocPrint(gpa, "referenced in {s}", .{
4997 self.objects.items[file].name,
4998 });
4999 notes.appendAssumeCapacity(.{ .msg = note });
5000 }
5001
5002 var err_msg = File.ErrorMsg{
5003 .msg = try std.fmt.allocPrint(gpa, "undefined reference to symbol {s}", .{sym_name}),
5004 };
5005 err_msg.notes = try notes.toOwnedSlice();
5006
5007 self.misc_errors.appendAssumeCapacity(err_msg);
5008 }
5009}
5010
5011fn reportSymbolCollision(
5012 self: *MachO,
5013 first: SymbolWithLoc,
5014 other: SymbolWithLoc,
5015) error{OutOfMemory}!void {
5016 const gpa = self.base.allocator;
5017 try self.misc_errors.ensureUnusedCapacity(gpa, 1);
5018
5019 var notes = try std.ArrayList(File.ErrorMsg).initCapacity(gpa, 2);
5020 defer notes.deinit();
5021
5022 if (first.getFile()) |file| {
5023 const note = try std.fmt.allocPrint(gpa, "first definition in {s}", .{
5024 self.objects.items[file].name,
5025 });
5026 notes.appendAssumeCapacity(.{ .msg = note });
5027 }
5028 if (other.getFile()) |file| {
5029 const note = try std.fmt.allocPrint(gpa, "next definition in {s}", .{
5030 self.objects.items[file].name,
5031 });
5032 notes.appendAssumeCapacity(.{ .msg = note });
5033 }
5034
5035 var err_msg = File.ErrorMsg{ .msg = try std.fmt.allocPrint(gpa, "symbol {s} defined multiple times", .{
5036 self.getSymbolName(first),
5037 }) };
5038 err_msg.notes = try notes.toOwnedSlice();
5039
5040 self.misc_errors.appendAssumeCapacity(err_msg);
5041}
5042
5043fn reportUnhandledSymbolType(self: *MachO, sym_with_loc: SymbolWithLoc) error{OutOfMemory}!void {
5044 const gpa = self.base.allocator;
5045 try self.misc_errors.ensureUnusedCapacity(gpa, 1);
5046
5047 const notes = try gpa.alloc(File.ErrorMsg, 1);
5048 errdefer gpa.free(notes);
5049
5050 const file = sym_with_loc.getFile().?;
5051 notes[0] = .{ .msg = try std.fmt.allocPrint(gpa, "defined in {s}", .{self.objects.items[file].name}) };
5052
5053 const sym = self.getSymbol(sym_with_loc);
5054 const sym_type = if (sym.stab())
5055 "stab"
5056 else if (sym.indr())
5057 "indirect"
5058 else if (sym.abs())
5059 "absolute"
5060 else
5061 unreachable;
5062
5063 self.misc_errors.appendAssumeCapacity(.{
5064 .msg = try std.fmt.allocPrint(gpa, "unhandled symbol type: '{s}' has type {s}", .{
5065 self.getSymbolName(sym_with_loc),
5066 sym_type,
5067 }),
5068 .notes = notes,
5069 });
5070}
5071
5072/// Binary search
5073pub fn bsearch(comptime T: type, haystack: []align(1) const T, predicate: anytype) usize {
37855074 if (!@hasDecl(@TypeOf(predicate), "predicate"))
37865075 @compileError("Predicate is required to define fn predicate(@This(), T) bool");
37875076
3788 if (start == haystack.len) return start;
5077 var min: usize = 0;
5078 var max: usize = haystack.len;
5079 while (min < max) {
5080 const index = (min + max) / 2;
5081 const curr = haystack[index];
5082 if (predicate.predicate(curr)) {
5083 min = index + 1;
5084 } else {
5085 max = index;
5086 }
5087 }
5088 return min;
5089}
37895090
3790 var i = start;
5091/// Linear search
5092pub fn lsearch(comptime T: type, haystack: []align(1) const T, predicate: anytype) usize {
5093 if (!@hasDecl(@TypeOf(predicate), "predicate"))
5094 @compileError("Predicate is required to define fn predicate(@This(), T) bool");
5095
5096 var i: usize = 0;
37915097 while (i < haystack.len) : (i += 1) {
37925098 if (predicate.predicate(haystack[i])) break;
37935099 }
37945100 return i;
37955101}
37965102
3797// fn snapshotState(self: *MachO) !void {
3798// const emit = self.base.options.emit orelse {
3799// log.debug("no emit directory found; skipping snapshot...", .{});
3800// return;
3801// };
3802
3803// const Snapshot = struct {
3804// const Node = struct {
3805// const Tag = enum {
3806// section_start,
3807// section_end,
3808// atom_start,
3809// atom_end,
3810// relocation,
3811
3812// pub fn jsonStringify(
3813// tag: Tag,
3814// options: std.json.StringifyOptions,
3815// out_stream: anytype,
3816// ) !void {
3817// _ = options;
3818// switch (tag) {
3819// .section_start => try out_stream.writeAll("\"section_start\""),
3820// .section_end => try out_stream.writeAll("\"section_end\""),
3821// .atom_start => try out_stream.writeAll("\"atom_start\""),
3822// .atom_end => try out_stream.writeAll("\"atom_end\""),
3823// .relocation => try out_stream.writeAll("\"relocation\""),
3824// }
3825// }
3826// };
3827// const Payload = struct {
3828// name: []const u8 = "",
3829// aliases: [][]const u8 = &[0][]const u8{},
3830// is_global: bool = false,
3831// target: u64 = 0,
3832// };
3833// address: u64,
3834// tag: Tag,
3835// payload: Payload,
3836// };
3837// timestamp: i128,
3838// nodes: []Node,
3839// };
3840
3841// var arena_allocator = std.heap.ArenaAllocator.init(self.base.allocator);
3842// defer arena_allocator.deinit();
3843// const arena = arena_allocator.allocator();
3844
3845// const out_file = try emit.directory.handle.createFile("snapshots.json", .{
3846// .truncate = false,
3847// .read = true,
3848// });
3849// defer out_file.close();
3850
3851// if (out_file.seekFromEnd(-1)) {
3852// try out_file.writer().writeByte(',');
3853// } else |err| switch (err) {
3854// error.Unseekable => try out_file.writer().writeByte('['),
3855// else => |e| return e,
3856// }
3857// const writer = out_file.writer();
3858
3859// var snapshot = Snapshot{
3860// .timestamp = std.time.nanoTimestamp(),
3861// .nodes = undefined,
3862// };
3863// var nodes = std.ArrayList(Snapshot.Node).init(arena);
3864
3865// for (self.section_ordinals.keys()) |key| {
3866// const sect = self.getSection(key);
3867// const sect_name = try std.fmt.allocPrint(arena, "{s},{s}", .{ sect.segName(), sect.sectName() });
3868// try nodes.append(.{
3869// .address = sect.addr,
3870// .tag = .section_start,
3871// .payload = .{ .name = sect_name },
3872// });
3873
3874// const is_tlv = sect.type_() == macho.S_THREAD_LOCAL_VARIABLES;
3875
3876// var atom: *Atom = self.atoms.get(key) orelse {
3877// try nodes.append(.{
3878// .address = sect.addr + sect.size,
3879// .tag = .section_end,
3880// .payload = .{},
3881// });
3882// continue;
3883// };
3884
3885// while (atom.prev) |prev| {
3886// atom = prev;
3887// }
3888
3889// while (true) {
3890// const atom_sym = atom.getSymbol(self);
3891// var node = Snapshot.Node{
3892// .address = atom_sym.n_value,
3893// .tag = .atom_start,
3894// .payload = .{
3895// .name = atom.getName(self),
3896// .is_global = self.globals.contains(atom.getName(self)),
3897// },
3898// };
3899
3900// var aliases = std.ArrayList([]const u8).init(arena);
3901// for (atom.contained.items) |sym_off| {
3902// if (sym_off.offset == 0) {
3903// try aliases.append(self.getSymbolName(.{
3904// .sym_index = sym_off.sym_index,
3905// .file = atom.file,
3906// }));
3907// }
3908// }
3909// node.payload.aliases = aliases.toOwnedSlice();
3910// try nodes.append(node);
3911
3912// var relocs = try std.ArrayList(Snapshot.Node).initCapacity(arena, atom.relocs.items.len);
3913// for (atom.relocs.items) |rel| {
3914// const source_addr = blk: {
3915// const source_sym = atom.getSymbol(self);
3916// break :blk source_sym.n_value + rel.offset;
3917// };
3918// const target_addr = blk: {
3919// const target_atom = rel.getTargetAtom(self) orelse {
3920// // If there is no atom for target, we still need to check for special, atom-less
3921// // symbols such as `___dso_handle`.
3922// const target_name = self.getSymbolName(rel.target);
3923// if (self.globals.contains(target_name)) {
3924// const atomless_sym = self.getSymbol(rel.target);
3925// break :blk atomless_sym.n_value;
3926// }
3927// break :blk 0;
3928// };
3929// const target_sym = if (target_atom.isSymbolContained(rel.target, self))
3930// self.getSymbol(rel.target)
3931// else
3932// target_atom.getSymbol(self);
3933// const base_address: u64 = if (is_tlv) base_address: {
3934// const sect_id: u16 = sect_id: {
3935// if (self.tlv_data_section_index) |i| {
3936// break :sect_id i;
3937// } else if (self.tlv_bss_section_index) |i| {
3938// break :sect_id i;
3939// } else unreachable;
3940// };
3941// break :base_address self.getSection(.{
3942// .seg = self.data_segment_cmd_index.?,
3943// .sect = sect_id,
3944// }).addr;
3945// } else 0;
3946// break :blk target_sym.n_value - base_address;
3947// };
3948
3949// relocs.appendAssumeCapacity(.{
3950// .address = source_addr,
3951// .tag = .relocation,
3952// .payload = .{ .target = target_addr },
3953// });
3954// }
3955
3956// if (atom.contained.items.len == 0) {
3957// try nodes.appendSlice(relocs.items);
3958// } else {
3959// // Need to reverse iteration order of relocs since by default for relocatable sources
3960// // they come in reverse. For linking, this doesn't matter in any way, however, for
3961// // arranging the memoryline for displaying it does.
3962// std.mem.reverse(Snapshot.Node, relocs.items);
3963
3964// var next_i: usize = 0;
3965// var last_rel: usize = 0;
3966// while (next_i < atom.contained.items.len) : (next_i += 1) {
3967// const loc = SymbolWithLoc{
3968// .sym_index = atom.contained.items[next_i].sym_index,
3969// .file = atom.file,
3970// };
3971// const cont_sym = self.getSymbol(loc);
3972// const cont_sym_name = self.getSymbolName(loc);
3973// var contained_node = Snapshot.Node{
3974// .address = cont_sym.n_value,
3975// .tag = .atom_start,
3976// .payload = .{
3977// .name = cont_sym_name,
3978// .is_global = self.globals.contains(cont_sym_name),
3979// },
3980// };
3981
3982// // Accumulate aliases
3983// var inner_aliases = std.ArrayList([]const u8).init(arena);
3984// while (true) {
3985// if (next_i + 1 >= atom.contained.items.len) break;
3986// const next_sym_loc = SymbolWithLoc{
3987// .sym_index = atom.contained.items[next_i + 1].sym_index,
3988// .file = atom.file,
3989// };
3990// const next_sym = self.getSymbol(next_sym_loc);
3991// if (next_sym.n_value != cont_sym.n_value) break;
3992// const next_sym_name = self.getSymbolName(next_sym_loc);
3993// if (self.globals.contains(next_sym_name)) {
3994// try inner_aliases.append(contained_node.payload.name);
3995// contained_node.payload.name = next_sym_name;
3996// contained_node.payload.is_global = true;
3997// } else try inner_aliases.append(next_sym_name);
3998// next_i += 1;
3999// }
4000
4001// const cont_size = if (next_i + 1 < atom.contained.items.len)
4002// self.getSymbol(.{
4003// .sym_index = atom.contained.items[next_i + 1].sym_index,
4004// .file = atom.file,
4005// }).n_value - cont_sym.n_value
4006// else
4007// atom_sym.n_value + atom.size - cont_sym.n_value;
4008
4009// contained_node.payload.aliases = inner_aliases.toOwnedSlice();
4010// try nodes.append(contained_node);
4011
4012// for (relocs.items[last_rel..]) |rel| {
4013// if (rel.address >= cont_sym.n_value + cont_size) {
4014// break;
4015// }
4016// try nodes.append(rel);
4017// last_rel += 1;
4018// }
4019
4020// try nodes.append(.{
4021// .address = cont_sym.n_value + cont_size,
4022// .tag = .atom_end,
4023// .payload = .{},
4024// });
4025// }
4026// }
4027
4028// try nodes.append(.{
4029// .address = atom_sym.n_value + atom.size,
4030// .tag = .atom_end,
4031// .payload = .{},
4032// });
4033
4034// if (atom.next) |next| {
4035// atom = next;
4036// } else break;
4037// }
4038
4039// try nodes.append(.{
4040// .address = sect.addr + sect.size,
4041// .tag = .section_end,
4042// .payload = .{},
4043// });
4044// }
4045
4046// snapshot.nodes = nodes.toOwnedSlice();
4047
4048// try std.json.stringify(snapshot, .{}, writer);
4049// try writer.writeByte(']');
4050// }
5103pub fn logSegments(self: *MachO) void {
5104 log.debug("segments:", .{});
5105 for (self.segments.items, 0..) |segment, i| {
5106 log.debug(" segment({d}): {s} @{x} ({x}), sizeof({x})", .{
5107 i,
5108 segment.segName(),
5109 segment.fileoff,
5110 segment.vmaddr,
5111 segment.vmsize,
5112 });
5113 }
5114}
40515115
40525116pub fn logSections(self: *MachO) void {
40535117 log.debug("sections:", .{});
......@@ -4063,9 +5127,7 @@ pub fn logSections(self: *MachO) void {
40635127 }
40645128}
40655129
4066fn logSymAttributes(sym: macho.nlist_64, buf: *[4]u8) []const u8 {
4067 @memset(buf[0..4], '_');
4068 @memset(buf[4..], ' ');
5130fn logSymAttributes(sym: macho.nlist_64, buf: []u8) []const u8 {
40695131 if (sym.sect()) {
40705132 buf[0] = 's';
40715133 }
......@@ -4088,56 +5150,110 @@ fn logSymAttributes(sym: macho.nlist_64, buf: *[4]u8) []const u8 {
40885150pub fn logSymtab(self: *MachO) void {
40895151 var buf: [4]u8 = undefined;
40905152
4091 log.debug("symtab:", .{});
5153 const scoped_log = std.log.scoped(.symtab);
5154
5155 scoped_log.debug("locals:", .{});
5156 for (self.objects.items, 0..) |object, id| {
5157 scoped_log.debug(" object({d}): {s}", .{ id, object.name });
5158 if (object.in_symtab == null) continue;
5159 for (object.symtab, 0..) |sym, sym_id| {
5160 @memset(&buf, '_');
5161 scoped_log.debug(" %{d}: {s} @{x} in sect({d}), {s}", .{
5162 sym_id,
5163 object.getSymbolName(@as(u32, @intCast(sym_id))),
5164 sym.n_value,
5165 sym.n_sect,
5166 logSymAttributes(sym, &buf),
5167 });
5168 }
5169 }
5170 scoped_log.debug(" object(-1)", .{});
40925171 for (self.locals.items, 0..) |sym, sym_id| {
4093 const where = if (sym.undf() and !sym.tentative()) "ord" else "sect";
4094 const def_index = if (sym.undf() and !sym.tentative())
4095 @divTrunc(sym.n_desc, macho.N_SYMBOL_RESOLVER)
4096 else
4097 sym.n_sect + 1;
4098 log.debug(" %{d}: {?s} @{x} in {s}({d}), {s}", .{
5172 if (sym.undf()) continue;
5173 scoped_log.debug(" %{d}: {s} @{x} in sect({d}), {s}", .{
40995174 sym_id,
4100 self.strtab.get(sym.n_strx),
5175 self.strtab.get(sym.n_strx).?,
41015176 sym.n_value,
4102 where,
4103 def_index,
5177 sym.n_sect,
41045178 logSymAttributes(sym, &buf),
41055179 });
41065180 }
41075181
4108 log.debug("globals table:", .{});
4109 for (self.globals.items) |global| {
4110 const name = self.getSymbolName(global);
4111 log.debug(" {s} => %{d} in object({?d})", .{ name, global.sym_index, global.file });
5182 scoped_log.debug("exports:", .{});
5183 for (self.globals.items, 0..) |global, i| {
5184 const sym = self.getSymbol(global);
5185 if (sym.undf()) continue;
5186 if (sym.n_desc == MachO.N_DEAD) continue;
5187 scoped_log.debug(" %{d}: {s} @{x} in sect({d}), {s} (def in object({?}))", .{
5188 i,
5189 self.getSymbolName(global),
5190 sym.n_value,
5191 sym.n_sect,
5192 logSymAttributes(sym, &buf),
5193 global.file,
5194 });
41125195 }
41135196
4114 log.debug("GOT entries:", .{});
4115 log.debug("{}", .{self.got_table});
5197 scoped_log.debug("imports:", .{});
5198 for (self.globals.items, 0..) |global, i| {
5199 const sym = self.getSymbol(global);
5200 if (!sym.undf()) continue;
5201 if (sym.n_desc == MachO.N_DEAD) continue;
5202 const ord = @divTrunc(sym.n_desc, macho.N_SYMBOL_RESOLVER);
5203 scoped_log.debug(" %{d}: {s} @{x} in ord({d}), {s}", .{
5204 i,
5205 self.getSymbolName(global),
5206 sym.n_value,
5207 ord,
5208 logSymAttributes(sym, &buf),
5209 });
5210 }
41165211
4117 log.debug("stubs entries:", .{});
4118 log.debug("{}", .{self.stub_table});
4119}
5212 scoped_log.debug("GOT entries:", .{});
5213 scoped_log.debug("{}", .{self.got_table});
41205214
4121pub fn logAtoms(self: *MachO) void {
4122 log.debug("atoms:", .{});
5215 scoped_log.debug("TLV pointers:", .{});
5216 scoped_log.debug("{}", .{self.tlv_ptr_table});
41235217
4124 const slice = self.sections.slice();
4125 for (slice.items(.last_atom_index), 0..) |last_atom_index, i| {
4126 var atom_index = last_atom_index orelse continue;
4127 const header = slice.items(.header)[i];
5218 scoped_log.debug("stubs entries:", .{});
5219 scoped_log.debug("{}", .{self.stub_table});
41285220
4129 while (true) {
5221 scoped_log.debug("thunks:", .{});
5222 for (self.thunks.items, 0..) |thunk, i| {
5223 scoped_log.debug(" thunk({d})", .{i});
5224 const slice = thunk.targets.slice();
5225 for (slice.items(.tag), slice.items(.target), 0..) |tag, target, j| {
5226 const atom_index = @as(u32, @intCast(thunk.getStartAtomIndex() + j));
41305227 const atom = self.getAtom(atom_index);
4131 if (atom.prev_index) |prev_index| {
4132 atom_index = prev_index;
4133 } else break;
5228 const atom_sym = self.getSymbol(atom.getSymbolWithLoc());
5229 const target_addr = switch (tag) {
5230 .stub => self.getStubsEntryAddress(target).?,
5231 .atom => self.getSymbol(target).n_value,
5232 };
5233 scoped_log.debug(" {d}@{x} => {s}({s}@{x})", .{
5234 j,
5235 atom_sym.n_value,
5236 @tagName(tag),
5237 self.getSymbolName(target),
5238 target_addr,
5239 });
41345240 }
5241 }
5242}
5243
5244pub fn logAtoms(self: *MachO) void {
5245 log.debug("atoms:", .{});
5246 const slice = self.sections.slice();
5247 for (slice.items(.first_atom_index), 0..) |first_atom_index, sect_id| {
5248 var atom_index = first_atom_index orelse continue;
5249 const header = slice.items(.header)[sect_id];
41355250
41365251 log.debug("{s},{s}", .{ header.segName(), header.sectName() });
41375252
41385253 while (true) {
4139 self.logAtom(atom_index);
41405254 const atom = self.getAtom(atom_index);
5255 self.logAtom(atom_index, log);
5256
41415257 if (atom.next_index) |next_index| {
41425258 atom_index = next_index;
41435259 } else break;
......@@ -4145,16 +5261,242 @@ pub fn logAtoms(self: *MachO) void {
41455261 }
41465262}
41475263
4148pub fn logAtom(self: *MachO, atom_index: Atom.Index) void {
5264pub fn logAtom(self: *MachO, atom_index: Atom.Index, logger: anytype) void {
5265 if (!build_options.enable_logging) return;
5266
41495267 const atom = self.getAtom(atom_index);
4150 const sym = atom.getSymbol(self);
4151 const sym_name = atom.getName(self);
4152 log.debug(" ATOM(%{?d}, '{s}') @ {x} sizeof({x}) in object({?d}) in sect({d})", .{
4153 atom.getSymbolIndex(),
5268 const sym = self.getSymbol(atom.getSymbolWithLoc());
5269 const sym_name = self.getSymbolName(atom.getSymbolWithLoc());
5270 logger.debug(" ATOM({d}, %{d}, '{s}') @ {x} (sizeof({x}), alignof({x})) in object({?}) in sect({d})", .{
5271 atom_index,
5272 atom.sym_index,
41545273 sym_name,
41555274 sym.n_value,
41565275 atom.size,
4157 atom.file,
4158 sym.n_sect + 1,
5276 atom.alignment,
5277 atom.getFile(),
5278 sym.n_sect,
41595279 });
5280
5281 if (atom.getFile() != null) {
5282 var it = Atom.getInnerSymbolsIterator(self, atom_index);
5283 while (it.next()) |sym_loc| {
5284 const inner = self.getSymbol(sym_loc);
5285 const inner_name = self.getSymbolName(sym_loc);
5286 const offset = Atom.calcInnerSymbolOffset(self, atom_index, sym_loc.sym_index);
5287
5288 logger.debug(" (%{d}, '{s}') @ {x} ({x})", .{
5289 sym_loc.sym_index,
5290 inner_name,
5291 inner.n_value,
5292 offset,
5293 });
5294 }
5295
5296 if (Atom.getSectionAlias(self, atom_index)) |sym_loc| {
5297 const alias = self.getSymbol(sym_loc);
5298 const alias_name = self.getSymbolName(sym_loc);
5299
5300 logger.debug(" (%{d}, '{s}') @ {x} ({x})", .{
5301 sym_loc.sym_index,
5302 alias_name,
5303 alias.n_value,
5304 0,
5305 });
5306 }
5307 }
41605308}
5309
5310pub const base_tag: File.Tag = File.Tag.macho;
5311pub const N_DEAD: u16 = @as(u16, @bitCast(@as(i16, -1)));
5312
5313/// Mode of operation of the linker.
5314pub const Mode = enum {
5315 /// Incremental mode will preallocate segments/sections and is compatible with
5316 /// watch and HCS modes of operation.
5317 incremental,
5318 /// Zld mode will link relocatables in a traditional, one-shot
5319 /// fashion (default for LLVM backend). It acts as a drop-in replacement for
5320 /// LLD.
5321 zld,
5322};
5323
5324pub const Section = struct {
5325 header: macho.section_64,
5326 segment_index: u8,
5327 first_atom_index: ?Atom.Index = null,
5328 last_atom_index: ?Atom.Index = null,
5329
5330 /// A list of atoms that have surplus capacity. This list can have false
5331 /// positives, as functions grow and shrink over time, only sometimes being added
5332 /// or removed from the freelist.
5333 ///
5334 /// An atom has surplus capacity when its overcapacity value is greater than
5335 /// padToIdeal(minimum_atom_size). That is, when it has so
5336 /// much extra capacity, that we could fit a small new symbol in it, itself with
5337 /// ideal_capacity or more.
5338 ///
5339 /// Ideal capacity is defined by size + (size / ideal_factor).
5340 ///
5341 /// Overcapacity is measured by actual_capacity - ideal_capacity. Note that
5342 /// overcapacity can be negative. A simple way to have negative overcapacity is to
5343 /// allocate a fresh atom, which will have ideal capacity, and then grow it
5344 /// by 1 byte. It will then have -1 overcapacity.
5345 free_list: std.ArrayListUnmanaged(Atom.Index) = .{},
5346};
5347
5348const is_hot_update_compatible = switch (builtin.target.os.tag) {
5349 .macos => true,
5350 else => false,
5351};
5352
5353const LazySymbolTable = std.AutoArrayHashMapUnmanaged(Module.Decl.OptionalIndex, LazySymbolMetadata);
5354
5355const LazySymbolMetadata = struct {
5356 const State = enum { unused, pending_flush, flushed };
5357 text_atom: Atom.Index = undefined,
5358 data_const_atom: Atom.Index = undefined,
5359 text_state: State = .unused,
5360 data_const_state: State = .unused,
5361};
5362
5363const TlvSymbolTable = std.AutoArrayHashMapUnmanaged(SymbolWithLoc, Atom.Index);
5364
5365const DeclMetadata = struct {
5366 atom: Atom.Index,
5367 section: u8,
5368 /// A list of all exports aliases of this Decl.
5369 /// TODO do we actually need this at all?
5370 exports: std.ArrayListUnmanaged(u32) = .{},
5371
5372 fn getExport(m: DeclMetadata, macho_file: *const MachO, name: []const u8) ?u32 {
5373 for (m.exports.items) |exp| {
5374 if (mem.eql(u8, name, macho_file.getSymbolName(.{ .sym_index = exp }))) return exp;
5375 }
5376 return null;
5377 }
5378
5379 fn getExportPtr(m: *DeclMetadata, macho_file: *MachO, name: []const u8) ?*u32 {
5380 for (m.exports.items) |*exp| {
5381 if (mem.eql(u8, name, macho_file.getSymbolName(.{ .sym_index = exp.* }))) return exp;
5382 }
5383 return null;
5384 }
5385};
5386
5387const BindingTable = std.AutoArrayHashMapUnmanaged(Atom.Index, std.ArrayListUnmanaged(Atom.Binding));
5388const UnnamedConstTable = std.AutoArrayHashMapUnmanaged(Module.Decl.Index, std.ArrayListUnmanaged(Atom.Index));
5389const RebaseTable = std.AutoArrayHashMapUnmanaged(Atom.Index, std.ArrayListUnmanaged(u32));
5390const RelocationTable = std.AutoArrayHashMapUnmanaged(Atom.Index, std.ArrayListUnmanaged(Relocation));
5391
5392pub const ResolveAction = struct {
5393 kind: Kind,
5394 target: SymbolWithLoc,
5395
5396 const Kind = enum {
5397 none,
5398 add_got,
5399 add_stub,
5400 };
5401};
5402
5403pub const SymbolWithLoc = extern struct {
5404 // Index into the respective symbol table.
5405 sym_index: u32,
5406
5407 // 0 means it's a synthetic global.
5408 file: u32 = 0,
5409
5410 pub fn getFile(self: SymbolWithLoc) ?u32 {
5411 if (self.file == 0) return null;
5412 return self.file - 1;
5413 }
5414
5415 pub fn eql(self: SymbolWithLoc, other: SymbolWithLoc) bool {
5416 return self.file == other.file and self.sym_index == other.sym_index;
5417 }
5418};
5419
5420const HotUpdateState = struct {
5421 mach_task: ?std.os.darwin.MachTask = null,
5422};
5423
5424/// When allocating, the ideal_capacity is calculated by
5425/// actual_capacity + (actual_capacity / ideal_factor)
5426const ideal_factor = 3;
5427
5428/// In order for a slice of bytes to be considered eligible to keep metadata pointing at
5429/// it as a possible place to put new symbols, it must have enough room for this many bytes
5430/// (plus extra for reserved capacity).
5431const minimum_text_block_size = 64;
5432pub const min_text_capacity = padToIdeal(minimum_text_block_size);
5433
5434/// Default virtual memory offset corresponds to the size of __PAGEZERO segment and
5435/// start of __TEXT segment.
5436pub const default_pagezero_vmsize: u64 = 0x100000000;
5437
5438/// We commit 0x1000 = 4096 bytes of space to the header and
5439/// the table of load commands. This should be plenty for any
5440/// potential future extensions.
5441pub const default_headerpad_size: u32 = 0x1000;
5442
5443const MachO = @This();
5444
5445const std = @import("std");
5446const build_options = @import("build_options");
5447const builtin = @import("builtin");
5448const assert = std.debug.assert;
5449const dwarf = std.dwarf;
5450const fs = std.fs;
5451const log = std.log.scoped(.link);
5452const macho = std.macho;
5453const math = std.math;
5454const mem = std.mem;
5455const meta = std.meta;
5456
5457const aarch64 = @import("../arch/aarch64/bits.zig");
5458const calcUuid = @import("MachO/uuid.zig").calcUuid;
5459const codegen = @import("../codegen.zig");
5460const dead_strip = @import("MachO/dead_strip.zig");
5461const fat = @import("MachO/fat.zig");
5462const link = @import("../link.zig");
5463const llvm_backend = @import("../codegen/llvm.zig");
5464const load_commands = @import("MachO/load_commands.zig");
5465const stubs = @import("MachO/stubs.zig");
5466const tapi = @import("tapi.zig");
5467const target_util = @import("../target.zig");
5468const thunks = @import("MachO/thunks.zig");
5469const trace = @import("../tracy.zig").trace;
5470const zld = @import("MachO/zld.zig");
5471
5472const Air = @import("../Air.zig");
5473const Allocator = mem.Allocator;
5474const Archive = @import("MachO/Archive.zig");
5475pub const Atom = @import("MachO/Atom.zig");
5476const Cache = std.Build.Cache;
5477const CodeSignature = @import("MachO/CodeSignature.zig");
5478const Compilation = @import("../Compilation.zig");
5479const Dwarf = File.Dwarf;
5480const DwarfInfo = @import("MachO/DwarfInfo.zig");
5481const Dylib = @import("MachO/Dylib.zig");
5482const File = link.File;
5483const Object = @import("MachO/Object.zig");
5484const LibStub = tapi.LibStub;
5485const Liveness = @import("../Liveness.zig");
5486const LlvmObject = @import("../codegen/llvm.zig").Object;
5487const Md5 = std.crypto.hash.Md5;
5488const Module = @import("../Module.zig");
5489const InternPool = @import("../InternPool.zig");
5490const Platform = load_commands.Platform;
5491const Relocation = @import("MachO/Relocation.zig");
5492const StringTable = @import("strtab.zig").StringTable;
5493const TableSection = @import("table_section.zig").TableSection;
5494const Trie = @import("MachO/Trie.zig");
5495const Type = @import("../type.zig").Type;
5496const TypedValue = @import("../TypedValue.zig");
5497const Value = @import("../value.zig").Value;
5498
5499pub const DebugSymbols = @import("MachO/DebugSymbols.zig");
5500pub const Bind = @import("MachO/dyld_info/bind.zig").Bind(*const MachO, SymbolWithLoc);
5501pub const LazyBind = @import("MachO/dyld_info/bind.zig").LazyBind(*const MachO, SymbolWithLoc);
5502pub const Rebase = @import("MachO/dyld_info/Rebase.zig");
src/link/MachO/Archive.zig+24-40
......@@ -1,15 +1,3 @@
1const Archive = @This();
2
3const std = @import("std");
4const assert = std.debug.assert;
5const fs = std.fs;
6const log = std.log.scoped(.link);
7const macho = std.macho;
8const mem = std.mem;
9
10const Allocator = mem.Allocator;
11const Object = @import("Object.zig");
12
131file: fs.File,
142fat_offset: u64,
153name: []const u8,
......@@ -87,6 +75,13 @@ const ar_hdr = extern struct {
8775 }
8876};
8977
78pub fn isArchive(file: fs.File, fat_offset: u64) bool {
79 const reader = file.reader();
80 const magic = reader.readBytesNoEof(SARMAG) catch return false;
81 defer file.seekTo(fat_offset) catch {};
82 return mem.eql(u8, &magic, ARMAG);
83}
84
9085pub fn deinit(self: *Archive, allocator: Allocator) void {
9186 self.file.close();
9287 for (self.toc.keys()) |*key| {
......@@ -100,21 +95,8 @@ pub fn deinit(self: *Archive, allocator: Allocator) void {
10095}
10196
10297pub fn parse(self: *Archive, allocator: Allocator, reader: anytype) !void {
103 const magic = try reader.readBytesNoEof(SARMAG);
104 if (!mem.eql(u8, &magic, ARMAG)) {
105 log.debug("invalid magic: expected '{s}', found '{s}'", .{ ARMAG, magic });
106 return error.NotArchive;
107 }
108
98 _ = try reader.readBytesNoEof(SARMAG);
10999 self.header = try reader.readStruct(ar_hdr);
110 if (!mem.eql(u8, &self.header.ar_fmag, ARFMAG)) {
111 log.debug("invalid header delimiter: expected '{s}', found '{s}'", .{
112 ARFMAG,
113 self.header.ar_fmag,
114 });
115 return error.NotArchive;
116 }
117
118100 const name_or_length = try self.header.nameOrLength();
119101 var embedded_name = try parseName(allocator, name_or_length, reader);
120102 log.debug("parsing archive '{s}' at '{s}'", .{ embedded_name, self.name });
......@@ -146,7 +128,7 @@ fn parseTableOfContents(self: *Archive, allocator: Allocator, reader: anytype) !
146128 defer allocator.free(symtab);
147129
148130 reader.readNoEof(symtab) catch {
149 log.err("incomplete symbol table: expected symbol table of length 0x{x}", .{symtab_size});
131 log.debug("incomplete symbol table: expected symbol table of length 0x{x}", .{symtab_size});
150132 return error.MalformedArchive;
151133 };
152134
......@@ -155,7 +137,7 @@ fn parseTableOfContents(self: *Archive, allocator: Allocator, reader: anytype) !
155137 defer allocator.free(strtab);
156138
157139 reader.readNoEof(strtab) catch {
158 log.err("incomplete symbol table: expected string table of length 0x{x}", .{strtab_size});
140 log.debug("incomplete symbol table: expected string table of length 0x{x}", .{strtab_size});
159141 return error.MalformedArchive;
160142 };
161143
......@@ -182,22 +164,12 @@ fn parseTableOfContents(self: *Archive, allocator: Allocator, reader: anytype) !
182164 }
183165}
184166
185pub fn parseObject(
186 self: Archive,
187 gpa: Allocator,
188 cpu_arch: std.Target.Cpu.Arch,
189 offset: u32,
190) !Object {
167pub fn parseObject(self: Archive, gpa: Allocator, offset: u32) !Object {
191168 const reader = self.file.reader();
192169 try reader.context.seekTo(self.fat_offset + offset);
193170
194171 const object_header = try reader.readStruct(ar_hdr);
195172
196 if (!mem.eql(u8, &object_header.ar_fmag, ARFMAG)) {
197 log.err("invalid header delimiter: expected '{s}', found '{s}'", .{ ARFMAG, object_header.ar_fmag });
198 return error.MalformedArchive;
199 }
200
201173 const name_or_length = try object_header.nameOrLength();
202174 const object_name = try parseName(gpa, name_or_length, reader);
203175 defer gpa.free(object_name);
......@@ -227,7 +199,19 @@ pub fn parseObject(
227199 .contents = contents,
228200 };
229201
230 try object.parse(gpa, cpu_arch);
202 try object.parse(gpa);
231203
232204 return object;
233205}
206
207const Archive = @This();
208
209const std = @import("std");
210const assert = std.debug.assert;
211const fs = std.fs;
212const log = std.log.scoped(.link);
213const macho = std.macho;
214const mem = std.mem;
215
216const Allocator = mem.Allocator;
217const Object = @import("Object.zig");
src/link/MachO/Atom.zig+1120-43
......@@ -1,42 +1,39 @@
1const Atom = @This();
2
3const std = @import("std");
4const build_options = @import("build_options");
5const aarch64 = @import("../../arch/aarch64/bits.zig");
6const assert = std.debug.assert;
7const log = std.log.scoped(.link);
8const macho = std.macho;
9const math = std.math;
10const mem = std.mem;
11const meta = std.meta;
12const trace = @import("../../tracy.zig").trace;
13
14const Allocator = mem.Allocator;
15const Arch = std.Target.Cpu.Arch;
16const MachO = @import("../MachO.zig");
17pub const Relocation = @import("Relocation.zig");
18const SymbolWithLoc = MachO.SymbolWithLoc;
19
20/// Each decl always gets a local symbol with the fully qualified name.
21/// The vaddr and size are found here directly.
22/// The file offset is found by computing the vaddr offset from the section vaddr
23/// the symbol references, and adding that to the file offset of the section.
24/// If this field is 0, it means the codegen size = 0 and there is no symbol or
1/// Each Atom always gets a symbol with the fully qualified name.
2/// The symbol can reside in any object file context structure in `symtab` array
3/// (see `Object`), or if the symbol is a synthetic symbol such as a GOT cell or
4/// a stub trampoline, it can be found in the linkers `locals` arraylist.
5/// If this field is 0 and file is 0, it means the codegen size = 0 and there is no symbol or
256/// offset table entry.
26sym_index: u32,
7sym_index: u32 = 0,
278
28/// null means symbol defined by Zig source.
29file: ?u32,
9/// 0 means an Atom is a synthetic Atom such as a GOT cell defined by the linker.
10/// Otherwise, it is the index into appropriate object file (indexing from 1).
11/// Prefer using `getFile()` helper to get the file index out rather than using
12/// the field directly.
13file: u32 = 0,
14
15/// If this Atom is not a synthetic Atom, i.e., references a subsection in an
16/// Object file, `inner_sym_index` and `inner_nsyms_trailing` tell where and if
17/// this Atom contains any additional symbol references that fall within this Atom's
18/// address range. These could for example be an alias symbol which can be used
19/// internally by the relocation records, or if the Object file couldn't be split
20/// into subsections, this Atom may encompass an entire input section.
21inner_sym_index: u32 = 0,
22inner_nsyms_trailing: u32 = 0,
3023
3124/// Size and alignment of this atom
3225/// Unlike in Elf, we need to store the size of this symbol as part of
3326/// the atom since macho.nlist_64 lacks this information.
34size: u64,
27size: u64 = 0,
28
29/// Alignment of this atom as a power of 2.
30/// For instance, aligmment of 0 should be read as 2^0 = 1 byte aligned.
31alignment: u32 = 0,
3532
3633/// Points to the previous and next neighbours
3734/// TODO use the same trick as with symbols: reserve index 0 as null atom
38next_index: ?Index,
39prev_index: ?Index,
35next_index: ?Index = null,
36prev_index: ?Index = null,
4037
4138pub const Index = u32;
4239
......@@ -45,13 +42,15 @@ pub const Binding = struct {
4542 offset: u64,
4643};
4744
48pub const SymbolAtOffset = struct {
49 sym_index: u32,
50 offset: u64,
51};
45/// Returns `null` if the Atom is a synthetic Atom.
46/// Otherwise, returns an index into an array of Objects.
47pub fn getFile(self: Atom) ?u32 {
48 if (self.file == 0) return null;
49 return self.file - 1;
50}
5251
5352pub fn getSymbolIndex(self: Atom) ?u32 {
54 if (self.sym_index == 0) return null;
53 if (self.getFile() == null and self.sym_index == 0) return null;
5554 return self.sym_index;
5655}
5756
......@@ -63,10 +62,7 @@ pub fn getSymbol(self: Atom, macho_file: *MachO) macho.nlist_64 {
6362/// Returns pointer-to-symbol referencing this atom.
6463pub fn getSymbolPtr(self: Atom, macho_file: *MachO) *macho.nlist_64 {
6564 const sym_index = self.getSymbolIndex().?;
66 return macho_file.getSymbolPtr(.{
67 .sym_index = sym_index,
68 .file = self.file,
69 });
65 return macho_file.getSymbolPtr(.{ .sym_index = sym_index, .file = self.file });
7066}
7167
7268pub fn getSymbolWithLoc(self: Atom) SymbolWithLoc {
......@@ -77,10 +73,7 @@ pub fn getSymbolWithLoc(self: Atom) SymbolWithLoc {
7773/// Returns the name of this atom.
7874pub fn getName(self: Atom, macho_file: *MachO) []const u8 {
7975 const sym_index = self.getSymbolIndex().?;
80 return macho_file.getSymbolName(.{
81 .sym_index = sym_index,
82 .file = self.file,
83 });
76 return macho_file.getSymbolName(.{ .sym_index = sym_index, .file = self.file });
8477}
8578
8679/// Returns how much room there is to grow in virtual address space.
......@@ -112,6 +105,147 @@ pub fn freeListEligible(self: Atom, macho_file: *MachO) bool {
112105 return surplus >= MachO.min_text_capacity;
113106}
114107
108pub fn getOutputSection(macho_file: *MachO, sect: macho.section_64) !?u8 {
109 const segname = sect.segName();
110 const sectname = sect.sectName();
111 const res: ?u8 = blk: {
112 if (mem.eql(u8, "__LLVM", segname)) {
113 log.debug("TODO LLVM section: type 0x{x}, name '{s},{s}'", .{
114 sect.flags, segname, sectname,
115 });
116 break :blk null;
117 }
118
119 // We handle unwind info separately.
120 if (mem.eql(u8, "__TEXT", segname) and mem.eql(u8, "__eh_frame", sectname)) {
121 break :blk null;
122 }
123 if (mem.eql(u8, "__LD", segname) and mem.eql(u8, "__compact_unwind", sectname)) {
124 break :blk null;
125 }
126
127 if (sect.isCode()) {
128 if (macho_file.text_section_index == null) {
129 macho_file.text_section_index = try macho_file.initSection("__TEXT", "__text", .{
130 .flags = macho.S_REGULAR |
131 macho.S_ATTR_PURE_INSTRUCTIONS |
132 macho.S_ATTR_SOME_INSTRUCTIONS,
133 });
134 }
135 break :blk macho_file.text_section_index.?;
136 }
137
138 if (sect.isDebug()) {
139 break :blk null;
140 }
141
142 switch (sect.type()) {
143 macho.S_4BYTE_LITERALS,
144 macho.S_8BYTE_LITERALS,
145 macho.S_16BYTE_LITERALS,
146 => {
147 break :blk macho_file.getSectionByName("__TEXT", "__const") orelse
148 try macho_file.initSection("__TEXT", "__const", .{});
149 },
150 macho.S_CSTRING_LITERALS => {
151 if (mem.startsWith(u8, sectname, "__objc")) {
152 break :blk macho_file.getSectionByName(segname, sectname) orelse
153 try macho_file.initSection(segname, sectname, .{});
154 }
155 break :blk macho_file.getSectionByName("__TEXT", "__cstring") orelse
156 try macho_file.initSection("__TEXT", "__cstring", .{
157 .flags = macho.S_CSTRING_LITERALS,
158 });
159 },
160 macho.S_MOD_INIT_FUNC_POINTERS,
161 macho.S_MOD_TERM_FUNC_POINTERS,
162 => {
163 break :blk macho_file.getSectionByName("__DATA_CONST", sectname) orelse
164 try macho_file.initSection("__DATA_CONST", sectname, .{
165 .flags = sect.flags,
166 });
167 },
168 macho.S_LITERAL_POINTERS,
169 macho.S_ZEROFILL,
170 macho.S_THREAD_LOCAL_VARIABLES,
171 macho.S_THREAD_LOCAL_VARIABLE_POINTERS,
172 macho.S_THREAD_LOCAL_REGULAR,
173 macho.S_THREAD_LOCAL_ZEROFILL,
174 => {
175 break :blk macho_file.getSectionByName(segname, sectname) orelse
176 try macho_file.initSection(segname, sectname, .{
177 .flags = sect.flags,
178 });
179 },
180 macho.S_COALESCED => {
181 break :blk macho_file.getSectionByName(segname, sectname) orelse
182 try macho_file.initSection(segname, sectname, .{});
183 },
184 macho.S_REGULAR => {
185 if (mem.eql(u8, segname, "__TEXT")) {
186 if (mem.eql(u8, sectname, "__rodata") or
187 mem.eql(u8, sectname, "__typelink") or
188 mem.eql(u8, sectname, "__itablink") or
189 mem.eql(u8, sectname, "__gosymtab") or
190 mem.eql(u8, sectname, "__gopclntab"))
191 {
192 break :blk macho_file.getSectionByName("__TEXT", sectname) orelse
193 try macho_file.initSection("__TEXT", sectname, .{});
194 }
195 }
196 if (mem.eql(u8, segname, "__DATA")) {
197 if (mem.eql(u8, sectname, "__const") or
198 mem.eql(u8, sectname, "__cfstring") or
199 mem.eql(u8, sectname, "__objc_classlist") or
200 mem.eql(u8, sectname, "__objc_imageinfo"))
201 {
202 break :blk macho_file.getSectionByName("__DATA_CONST", sectname) orelse
203 try macho_file.initSection("__DATA_CONST", sectname, .{});
204 } else if (mem.eql(u8, sectname, "__data")) {
205 if (macho_file.data_section_index == null) {
206 macho_file.data_section_index = try macho_file.initSection("__DATA", "__data", .{});
207 }
208 break :blk macho_file.data_section_index.?;
209 }
210 }
211 break :blk macho_file.getSectionByName(segname, sectname) orelse
212 try macho_file.initSection(segname, sectname, .{});
213 },
214 else => break :blk null,
215 }
216 };
217
218 // TODO we can do this directly in the selection logic above.
219 // Or is it not worth it?
220 if (macho_file.data_const_section_index == null) {
221 if (macho_file.getSectionByName("__DATA_CONST", "__const")) |index| {
222 macho_file.data_const_section_index = index;
223 }
224 }
225 if (macho_file.thread_vars_section_index == null) {
226 if (macho_file.getSectionByName("__DATA", "__thread_vars")) |index| {
227 macho_file.thread_vars_section_index = index;
228 }
229 }
230 if (macho_file.thread_data_section_index == null) {
231 if (macho_file.getSectionByName("__DATA", "__thread_data")) |index| {
232 macho_file.thread_data_section_index = index;
233 }
234 }
235 if (macho_file.thread_bss_section_index == null) {
236 if (macho_file.getSectionByName("__DATA", "__thread_bss")) |index| {
237 macho_file.thread_bss_section_index = index;
238 }
239 }
240 if (macho_file.bss_section_index == null) {
241 if (macho_file.getSectionByName("__DATA", "__bss")) |index| {
242 macho_file.bss_section_index = index;
243 }
244 }
245
246 return res;
247}
248
115249pub fn addRelocation(macho_file: *MachO, atom_index: Index, reloc: Relocation) !void {
116250 return addRelocations(macho_file, atom_index, &[_]Relocation{reloc});
117251}
......@@ -179,3 +313,946 @@ pub fn freeRelocations(macho_file: *MachO, atom_index: Index) void {
179313 var removed_bindings = macho_file.bindings.fetchOrderedRemove(atom_index);
180314 if (removed_bindings) |*bindings| bindings.value.deinit(gpa);
181315}
316
317const InnerSymIterator = struct {
318 sym_index: u32,
319 nsyms: u32,
320 file: u32,
321 pos: u32 = 0,
322
323 pub fn next(it: *@This()) ?SymbolWithLoc {
324 if (it.pos == it.nsyms) return null;
325 const res = SymbolWithLoc{ .sym_index = it.sym_index + it.pos, .file = it.file };
326 it.pos += 1;
327 return res;
328 }
329};
330
331/// Returns an iterator over potentially contained symbols.
332/// Panics when called on a synthetic Atom.
333pub fn getInnerSymbolsIterator(macho_file: *MachO, atom_index: Index) InnerSymIterator {
334 const atom = macho_file.getAtom(atom_index);
335 assert(atom.getFile() != null);
336 return .{
337 .sym_index = atom.inner_sym_index,
338 .nsyms = atom.inner_nsyms_trailing,
339 .file = atom.file,
340 };
341}
342
343/// Returns a section alias symbol if one is defined.
344/// An alias symbol is used to represent the start of an input section
345/// if there were no symbols defined within that range.
346/// Alias symbols are only used on x86_64.
347pub fn getSectionAlias(macho_file: *MachO, atom_index: Index) ?SymbolWithLoc {
348 const atom = macho_file.getAtom(atom_index);
349 assert(atom.getFile() != null);
350
351 const object = macho_file.objects.items[atom.getFile().?];
352 const nbase = @as(u32, @intCast(object.in_symtab.?.len));
353 const ntotal = @as(u32, @intCast(object.symtab.len));
354 var sym_index: u32 = nbase;
355 while (sym_index < ntotal) : (sym_index += 1) {
356 if (object.getAtomIndexForSymbol(sym_index)) |other_atom_index| {
357 if (other_atom_index == atom_index) return SymbolWithLoc{
358 .sym_index = sym_index,
359 .file = atom.file,
360 };
361 }
362 }
363 return null;
364}
365
366/// Given an index into a contained symbol within, calculates an offset wrt
367/// the start of this Atom.
368pub fn calcInnerSymbolOffset(macho_file: *MachO, atom_index: Index, sym_index: u32) u64 {
369 const atom = macho_file.getAtom(atom_index);
370 assert(atom.getFile() != null);
371
372 if (atom.sym_index == sym_index) return 0;
373
374 const object = macho_file.objects.items[atom.getFile().?];
375 const source_sym = object.getSourceSymbol(sym_index).?;
376 const base_addr = if (object.getSourceSymbol(atom.sym_index)) |sym|
377 sym.n_value
378 else blk: {
379 const nbase = @as(u32, @intCast(object.in_symtab.?.len));
380 const sect_id = @as(u8, @intCast(atom.sym_index - nbase));
381 const source_sect = object.getSourceSection(sect_id);
382 break :blk source_sect.addr;
383 };
384 return source_sym.n_value - base_addr;
385}
386
387pub fn scanAtomRelocs(macho_file: *MachO, atom_index: Index, relocs: []align(1) const macho.relocation_info) !void {
388 const arch = macho_file.base.options.target.cpu.arch;
389 const atom = macho_file.getAtom(atom_index);
390 assert(atom.getFile() != null); // synthetic atoms do not have relocs
391
392 return switch (arch) {
393 .aarch64 => scanAtomRelocsArm64(macho_file, atom_index, relocs),
394 .x86_64 => scanAtomRelocsX86(macho_file, atom_index, relocs),
395 else => unreachable,
396 };
397}
398
399const RelocContext = struct {
400 base_addr: i64 = 0,
401 base_offset: i32 = 0,
402};
403
404pub fn getRelocContext(macho_file: *MachO, atom_index: Index) RelocContext {
405 const atom = macho_file.getAtom(atom_index);
406 assert(atom.getFile() != null); // synthetic atoms do not have relocs
407
408 const object = macho_file.objects.items[atom.getFile().?];
409 if (object.getSourceSymbol(atom.sym_index)) |source_sym| {
410 const source_sect = object.getSourceSection(source_sym.n_sect - 1);
411 return .{
412 .base_addr = @as(i64, @intCast(source_sect.addr)),
413 .base_offset = @as(i32, @intCast(source_sym.n_value - source_sect.addr)),
414 };
415 }
416 const nbase = @as(u32, @intCast(object.in_symtab.?.len));
417 const sect_id = @as(u8, @intCast(atom.sym_index - nbase));
418 const source_sect = object.getSourceSection(sect_id);
419 return .{
420 .base_addr = @as(i64, @intCast(source_sect.addr)),
421 .base_offset = 0,
422 };
423}
424
425pub fn parseRelocTarget(macho_file: *MachO, ctx: struct {
426 object_id: u32,
427 rel: macho.relocation_info,
428 code: []const u8,
429 base_addr: i64 = 0,
430 base_offset: i32 = 0,
431}) SymbolWithLoc {
432 const tracy = trace(@src());
433 defer tracy.end();
434
435 const object = &macho_file.objects.items[ctx.object_id];
436 log.debug("parsing reloc target in object({d}) '{s}' ", .{ ctx.object_id, object.name });
437
438 const sym_index = if (ctx.rel.r_extern == 0) sym_index: {
439 const sect_id = @as(u8, @intCast(ctx.rel.r_symbolnum - 1));
440 const rel_offset = @as(u32, @intCast(ctx.rel.r_address - ctx.base_offset));
441
442 const address_in_section = if (ctx.rel.r_pcrel == 0) blk: {
443 break :blk if (ctx.rel.r_length == 3)
444 mem.readIntLittle(u64, ctx.code[rel_offset..][0..8])
445 else
446 mem.readIntLittle(u32, ctx.code[rel_offset..][0..4]);
447 } else blk: {
448 assert(macho_file.base.options.target.cpu.arch == .x86_64);
449 const correction: u3 = switch (@as(macho.reloc_type_x86_64, @enumFromInt(ctx.rel.r_type))) {
450 .X86_64_RELOC_SIGNED => 0,
451 .X86_64_RELOC_SIGNED_1 => 1,
452 .X86_64_RELOC_SIGNED_2 => 2,
453 .X86_64_RELOC_SIGNED_4 => 4,
454 else => unreachable,
455 };
456 const addend = mem.readIntLittle(i32, ctx.code[rel_offset..][0..4]);
457 const target_address = @as(i64, @intCast(ctx.base_addr)) + ctx.rel.r_address + 4 + correction + addend;
458 break :blk @as(u64, @intCast(target_address));
459 };
460
461 // Find containing atom
462 log.debug(" | locating symbol by address @{x} in section {d}", .{ address_in_section, sect_id });
463 break :sym_index object.getSymbolByAddress(address_in_section, sect_id);
464 } else object.reverse_symtab_lookup[ctx.rel.r_symbolnum];
465
466 const sym_loc = SymbolWithLoc{ .sym_index = sym_index, .file = ctx.object_id + 1 };
467 const sym = macho_file.getSymbol(sym_loc);
468 const target = if (sym.sect() and !sym.ext())
469 sym_loc
470 else if (object.getGlobal(sym_index)) |global_index|
471 macho_file.globals.items[global_index]
472 else
473 sym_loc;
474 log.debug(" | target %{d} ('{s}') in object({?d})", .{
475 target.sym_index,
476 macho_file.getSymbolName(target),
477 target.getFile(),
478 });
479 return target;
480}
481
482pub fn getRelocTargetAtomIndex(macho_file: *MachO, target: SymbolWithLoc) ?Index {
483 if (target.getFile() == null) {
484 const target_sym_name = macho_file.getSymbolName(target);
485 if (mem.eql(u8, "__mh_execute_header", target_sym_name)) return null;
486 if (mem.eql(u8, "___dso_handle", target_sym_name)) return null;
487
488 unreachable; // referenced symbol not found
489 }
490
491 const object = macho_file.objects.items[target.getFile().?];
492 return object.getAtomIndexForSymbol(target.sym_index);
493}
494
495fn scanAtomRelocsArm64(
496 macho_file: *MachO,
497 atom_index: Index,
498 relocs: []align(1) const macho.relocation_info,
499) !void {
500 for (relocs) |rel| {
501 const rel_type = @as(macho.reloc_type_arm64, @enumFromInt(rel.r_type));
502
503 switch (rel_type) {
504 .ARM64_RELOC_ADDEND, .ARM64_RELOC_SUBTRACTOR => continue,
505 else => {},
506 }
507
508 if (rel.r_extern == 0) continue;
509
510 const atom = macho_file.getAtom(atom_index);
511 const object = &macho_file.objects.items[atom.getFile().?];
512 const sym_index = object.reverse_symtab_lookup[rel.r_symbolnum];
513 const sym_loc = SymbolWithLoc{
514 .sym_index = sym_index,
515 .file = atom.file,
516 };
517
518 const target = if (object.getGlobal(sym_index)) |global_index|
519 macho_file.globals.items[global_index]
520 else
521 sym_loc;
522
523 switch (rel_type) {
524 .ARM64_RELOC_BRANCH26 => {
525 // TODO rewrite relocation
526 const sym = macho_file.getSymbol(target);
527 if (sym.undf()) try macho_file.addStubEntry(target);
528 },
529 .ARM64_RELOC_GOT_LOAD_PAGE21,
530 .ARM64_RELOC_GOT_LOAD_PAGEOFF12,
531 .ARM64_RELOC_POINTER_TO_GOT,
532 => {
533 // TODO rewrite relocation
534 try macho_file.addGotEntry(target);
535 },
536 .ARM64_RELOC_TLVP_LOAD_PAGE21,
537 .ARM64_RELOC_TLVP_LOAD_PAGEOFF12,
538 => {
539 const sym = macho_file.getSymbol(target);
540 if (sym.undf()) try macho_file.addTlvPtrEntry(target);
541 },
542 else => {},
543 }
544 }
545}
546
547fn scanAtomRelocsX86(
548 macho_file: *MachO,
549 atom_index: Index,
550 relocs: []align(1) const macho.relocation_info,
551) !void {
552 for (relocs) |rel| {
553 const rel_type = @as(macho.reloc_type_x86_64, @enumFromInt(rel.r_type));
554
555 switch (rel_type) {
556 .X86_64_RELOC_SUBTRACTOR => continue,
557 else => {},
558 }
559
560 if (rel.r_extern == 0) continue;
561
562 const atom = macho_file.getAtom(atom_index);
563 const object = &macho_file.objects.items[atom.getFile().?];
564 const sym_index = object.reverse_symtab_lookup[rel.r_symbolnum];
565 const sym_loc = SymbolWithLoc{
566 .sym_index = sym_index,
567 .file = atom.file,
568 };
569
570 const target = if (object.getGlobal(sym_index)) |global_index|
571 macho_file.globals.items[global_index]
572 else
573 sym_loc;
574
575 switch (rel_type) {
576 .X86_64_RELOC_BRANCH => {
577 // TODO rewrite relocation
578 const sym = macho_file.getSymbol(target);
579 if (sym.undf()) try macho_file.addStubEntry(target);
580 },
581 .X86_64_RELOC_GOT, .X86_64_RELOC_GOT_LOAD => {
582 // TODO rewrite relocation
583 try macho_file.addGotEntry(target);
584 },
585 .X86_64_RELOC_TLV => {
586 const sym = macho_file.getSymbol(target);
587 if (sym.undf()) try macho_file.addTlvPtrEntry(target);
588 },
589 else => {},
590 }
591 }
592}
593
594pub fn resolveRelocs(
595 macho_file: *MachO,
596 atom_index: Index,
597 atom_code: []u8,
598 atom_relocs: []align(1) const macho.relocation_info,
599) !void {
600 const arch = macho_file.base.options.target.cpu.arch;
601 const atom = macho_file.getAtom(atom_index);
602 assert(atom.getFile() != null); // synthetic atoms do not have relocs
603
604 log.debug("resolving relocations in ATOM(%{d}, '{s}')", .{
605 atom.sym_index,
606 macho_file.getSymbolName(atom.getSymbolWithLoc()),
607 });
608
609 const ctx = getRelocContext(macho_file, atom_index);
610
611 return switch (arch) {
612 .aarch64 => resolveRelocsArm64(macho_file, atom_index, atom_code, atom_relocs, ctx),
613 .x86_64 => resolveRelocsX86(macho_file, atom_index, atom_code, atom_relocs, ctx),
614 else => unreachable,
615 };
616}
617
618pub fn getRelocTargetAddress(macho_file: *MachO, target: SymbolWithLoc, is_tlv: bool) u64 {
619 const target_atom_index = getRelocTargetAtomIndex(macho_file, target) orelse {
620 // If there is no atom for target, we still need to check for special, atom-less
621 // symbols such as `___dso_handle`.
622 const target_name = macho_file.getSymbolName(target);
623 const atomless_sym = macho_file.getSymbol(target);
624 log.debug(" | atomless target '{s}'", .{target_name});
625 return atomless_sym.n_value;
626 };
627 const target_atom = macho_file.getAtom(target_atom_index);
628 log.debug(" | target ATOM(%{d}, '{s}') in object({?})", .{
629 target_atom.sym_index,
630 macho_file.getSymbolName(target_atom.getSymbolWithLoc()),
631 target_atom.getFile(),
632 });
633
634 const target_sym = macho_file.getSymbol(target_atom.getSymbolWithLoc());
635 assert(target_sym.n_desc != MachO.N_DEAD);
636
637 // If `target` is contained within the target atom, pull its address value.
638 const offset = if (target_atom.getFile() != null) blk: {
639 const object = macho_file.objects.items[target_atom.getFile().?];
640 break :blk if (object.getSourceSymbol(target.sym_index)) |_|
641 Atom.calcInnerSymbolOffset(macho_file, target_atom_index, target.sym_index)
642 else
643 0; // section alias
644 } else 0;
645 const base_address: u64 = if (is_tlv) base_address: {
646 // For TLV relocations, the value specified as a relocation is the displacement from the
647 // TLV initializer (either value in __thread_data or zero-init in __thread_bss) to the first
648 // defined TLV template init section in the following order:
649 // * wrt to __thread_data if defined, then
650 // * wrt to __thread_bss
651 // TODO remember to check what the mechanism was prior to HAS_TLV_INITIALIZERS in earlier versions of macOS
652 const sect_id: u16 = sect_id: {
653 if (macho_file.thread_data_section_index) |i| {
654 break :sect_id i;
655 } else if (macho_file.thread_bss_section_index) |i| {
656 break :sect_id i;
657 } else break :base_address 0;
658 };
659 break :base_address macho_file.sections.items(.header)[sect_id].addr;
660 } else 0;
661 return target_sym.n_value + offset - base_address;
662}
663
664fn resolveRelocsArm64(
665 macho_file: *MachO,
666 atom_index: Index,
667 atom_code: []u8,
668 atom_relocs: []align(1) const macho.relocation_info,
669 context: RelocContext,
670) !void {
671 const atom = macho_file.getAtom(atom_index);
672 const object = macho_file.objects.items[atom.getFile().?];
673
674 var addend: ?i64 = null;
675 var subtractor: ?SymbolWithLoc = null;
676
677 for (atom_relocs) |rel| {
678 const rel_type = @as(macho.reloc_type_arm64, @enumFromInt(rel.r_type));
679
680 switch (rel_type) {
681 .ARM64_RELOC_ADDEND => {
682 assert(addend == null);
683
684 log.debug(" RELA({s}) @ {x} => {x}", .{ @tagName(rel_type), rel.r_address, rel.r_symbolnum });
685
686 addend = rel.r_symbolnum;
687 continue;
688 },
689 .ARM64_RELOC_SUBTRACTOR => {
690 assert(subtractor == null);
691
692 log.debug(" RELA({s}) @ {x} => %{d} in object({?d})", .{
693 @tagName(rel_type),
694 rel.r_address,
695 rel.r_symbolnum,
696 atom.getFile(),
697 });
698
699 subtractor = parseRelocTarget(macho_file, .{
700 .object_id = atom.getFile().?,
701 .rel = rel,
702 .code = atom_code,
703 .base_addr = context.base_addr,
704 .base_offset = context.base_offset,
705 });
706 continue;
707 },
708 else => {},
709 }
710
711 const target = parseRelocTarget(macho_file, .{
712 .object_id = atom.getFile().?,
713 .rel = rel,
714 .code = atom_code,
715 .base_addr = context.base_addr,
716 .base_offset = context.base_offset,
717 });
718 const rel_offset = @as(u32, @intCast(rel.r_address - context.base_offset));
719
720 log.debug(" RELA({s}) @ {x} => %{d} ('{s}') in object({?})", .{
721 @tagName(rel_type),
722 rel.r_address,
723 target.sym_index,
724 macho_file.getSymbolName(target),
725 target.getFile(),
726 });
727
728 const source_addr = blk: {
729 const source_sym = macho_file.getSymbol(atom.getSymbolWithLoc());
730 break :blk source_sym.n_value + rel_offset;
731 };
732 const target_addr = blk: {
733 if (relocRequiresGot(macho_file, rel)) break :blk macho_file.getGotEntryAddress(target).?;
734 if (relocIsTlv(macho_file, rel) and macho_file.getSymbol(target).undf())
735 break :blk macho_file.getTlvPtrEntryAddress(target).?;
736 if (relocIsStub(macho_file, rel) and macho_file.getSymbol(target).undf())
737 break :blk macho_file.getStubsEntryAddress(target).?;
738 const is_tlv = is_tlv: {
739 const source_sym = macho_file.getSymbol(atom.getSymbolWithLoc());
740 const header = macho_file.sections.items(.header)[source_sym.n_sect - 1];
741 break :is_tlv header.type() == macho.S_THREAD_LOCAL_VARIABLES;
742 };
743 break :blk getRelocTargetAddress(macho_file, target, is_tlv);
744 };
745
746 log.debug(" | source_addr = 0x{x}", .{source_addr});
747
748 switch (rel_type) {
749 .ARM64_RELOC_BRANCH26 => {
750 log.debug(" source {s} (object({?})), target {s}", .{
751 macho_file.getSymbolName(atom.getSymbolWithLoc()),
752 atom.getFile(),
753 macho_file.getSymbolName(target),
754 });
755
756 const displacement = if (Relocation.calcPcRelativeDisplacementArm64(
757 source_addr,
758 target_addr,
759 )) |disp| blk: {
760 log.debug(" | target_addr = 0x{x}", .{target_addr});
761 break :blk disp;
762 } else |_| blk: {
763 const thunk_index = macho_file.thunk_table.get(atom_index).?;
764 const thunk = macho_file.thunks.items[thunk_index];
765 const thunk_sym_loc = if (macho_file.getSymbol(target).undf())
766 thunk.getTrampoline(macho_file, .stub, target).?
767 else
768 thunk.getTrampoline(macho_file, .atom, target).?;
769 const thunk_addr = macho_file.getSymbol(thunk_sym_loc).n_value;
770 log.debug(" | target_addr = 0x{x} (thunk)", .{thunk_addr});
771 break :blk try Relocation.calcPcRelativeDisplacementArm64(source_addr, thunk_addr);
772 };
773
774 const code = atom_code[rel_offset..][0..4];
775 var inst = aarch64.Instruction{
776 .unconditional_branch_immediate = mem.bytesToValue(meta.TagPayload(
777 aarch64.Instruction,
778 aarch64.Instruction.unconditional_branch_immediate,
779 ), code),
780 };
781 inst.unconditional_branch_immediate.imm26 = @as(u26, @truncate(@as(u28, @bitCast(displacement >> 2))));
782 mem.writeIntLittle(u32, code, inst.toU32());
783 },
784
785 .ARM64_RELOC_PAGE21,
786 .ARM64_RELOC_GOT_LOAD_PAGE21,
787 .ARM64_RELOC_TLVP_LOAD_PAGE21,
788 => {
789 const adjusted_target_addr = @as(u64, @intCast(@as(i64, @intCast(target_addr)) + (addend orelse 0)));
790
791 log.debug(" | target_addr = 0x{x}", .{adjusted_target_addr});
792
793 const pages = @as(u21, @bitCast(Relocation.calcNumberOfPages(source_addr, adjusted_target_addr)));
794 const code = atom_code[rel_offset..][0..4];
795 var inst = aarch64.Instruction{
796 .pc_relative_address = mem.bytesToValue(meta.TagPayload(
797 aarch64.Instruction,
798 aarch64.Instruction.pc_relative_address,
799 ), code),
800 };
801 inst.pc_relative_address.immhi = @as(u19, @truncate(pages >> 2));
802 inst.pc_relative_address.immlo = @as(u2, @truncate(pages));
803 mem.writeIntLittle(u32, code, inst.toU32());
804 addend = null;
805 },
806
807 .ARM64_RELOC_PAGEOFF12 => {
808 const adjusted_target_addr = @as(u64, @intCast(@as(i64, @intCast(target_addr)) + (addend orelse 0)));
809
810 log.debug(" | target_addr = 0x{x}", .{adjusted_target_addr});
811
812 const code = atom_code[rel_offset..][0..4];
813 if (Relocation.isArithmeticOp(code)) {
814 const off = try Relocation.calcPageOffset(adjusted_target_addr, .arithmetic);
815 var inst = aarch64.Instruction{
816 .add_subtract_immediate = mem.bytesToValue(meta.TagPayload(
817 aarch64.Instruction,
818 aarch64.Instruction.add_subtract_immediate,
819 ), code),
820 };
821 inst.add_subtract_immediate.imm12 = off;
822 mem.writeIntLittle(u32, code, inst.toU32());
823 } else {
824 var inst = aarch64.Instruction{
825 .load_store_register = mem.bytesToValue(meta.TagPayload(
826 aarch64.Instruction,
827 aarch64.Instruction.load_store_register,
828 ), code),
829 };
830 const off = try Relocation.calcPageOffset(adjusted_target_addr, switch (inst.load_store_register.size) {
831 0 => if (inst.load_store_register.v == 1)
832 Relocation.PageOffsetInstKind.load_store_128
833 else
834 Relocation.PageOffsetInstKind.load_store_8,
835 1 => .load_store_16,
836 2 => .load_store_32,
837 3 => .load_store_64,
838 });
839 inst.load_store_register.offset = off;
840 mem.writeIntLittle(u32, code, inst.toU32());
841 }
842 addend = null;
843 },
844
845 .ARM64_RELOC_GOT_LOAD_PAGEOFF12 => {
846 const code = atom_code[rel_offset..][0..4];
847 const adjusted_target_addr = @as(u64, @intCast(@as(i64, @intCast(target_addr)) + (addend orelse 0)));
848
849 log.debug(" | target_addr = 0x{x}", .{adjusted_target_addr});
850
851 const off = try Relocation.calcPageOffset(adjusted_target_addr, .load_store_64);
852 var inst: aarch64.Instruction = .{
853 .load_store_register = mem.bytesToValue(meta.TagPayload(
854 aarch64.Instruction,
855 aarch64.Instruction.load_store_register,
856 ), code),
857 };
858 inst.load_store_register.offset = off;
859 mem.writeIntLittle(u32, code, inst.toU32());
860 addend = null;
861 },
862
863 .ARM64_RELOC_TLVP_LOAD_PAGEOFF12 => {
864 const code = atom_code[rel_offset..][0..4];
865 const adjusted_target_addr = @as(u64, @intCast(@as(i64, @intCast(target_addr)) + (addend orelse 0)));
866
867 log.debug(" | target_addr = 0x{x}", .{adjusted_target_addr});
868
869 const RegInfo = struct {
870 rd: u5,
871 rn: u5,
872 size: u2,
873 };
874 const reg_info: RegInfo = blk: {
875 if (Relocation.isArithmeticOp(code)) {
876 const inst = mem.bytesToValue(meta.TagPayload(
877 aarch64.Instruction,
878 aarch64.Instruction.add_subtract_immediate,
879 ), code);
880 break :blk .{
881 .rd = inst.rd,
882 .rn = inst.rn,
883 .size = inst.sf,
884 };
885 } else {
886 const inst = mem.bytesToValue(meta.TagPayload(
887 aarch64.Instruction,
888 aarch64.Instruction.load_store_register,
889 ), code);
890 break :blk .{
891 .rd = inst.rt,
892 .rn = inst.rn,
893 .size = inst.size,
894 };
895 }
896 };
897
898 var inst = if (macho_file.tlv_ptr_table.lookup.contains(target)) aarch64.Instruction{
899 .load_store_register = .{
900 .rt = reg_info.rd,
901 .rn = reg_info.rn,
902 .offset = try Relocation.calcPageOffset(adjusted_target_addr, .load_store_64),
903 .opc = 0b01,
904 .op1 = 0b01,
905 .v = 0,
906 .size = reg_info.size,
907 },
908 } else aarch64.Instruction{
909 .add_subtract_immediate = .{
910 .rd = reg_info.rd,
911 .rn = reg_info.rn,
912 .imm12 = try Relocation.calcPageOffset(adjusted_target_addr, .arithmetic),
913 .sh = 0,
914 .s = 0,
915 .op = 0,
916 .sf = @as(u1, @truncate(reg_info.size)),
917 },
918 };
919 mem.writeIntLittle(u32, code, inst.toU32());
920 addend = null;
921 },
922
923 .ARM64_RELOC_POINTER_TO_GOT => {
924 log.debug(" | target_addr = 0x{x}", .{target_addr});
925 const result = math.cast(i32, @as(i64, @intCast(target_addr)) - @as(i64, @intCast(source_addr))) orelse
926 return error.Overflow;
927 mem.writeIntLittle(u32, atom_code[rel_offset..][0..4], @as(u32, @bitCast(result)));
928 },
929
930 .ARM64_RELOC_UNSIGNED => {
931 var ptr_addend = if (rel.r_length == 3)
932 mem.readIntLittle(i64, atom_code[rel_offset..][0..8])
933 else
934 mem.readIntLittle(i32, atom_code[rel_offset..][0..4]);
935
936 if (rel.r_extern == 0) {
937 const base_addr = if (target.sym_index >= object.source_address_lookup.len)
938 @as(i64, @intCast(object.getSourceSection(@as(u8, @intCast(rel.r_symbolnum - 1))).addr))
939 else
940 object.source_address_lookup[target.sym_index];
941 ptr_addend -= base_addr;
942 }
943
944 const result = blk: {
945 if (subtractor) |sub| {
946 const sym = macho_file.getSymbol(sub);
947 break :blk @as(i64, @intCast(target_addr)) - @as(i64, @intCast(sym.n_value)) + ptr_addend;
948 } else {
949 break :blk @as(i64, @intCast(target_addr)) + ptr_addend;
950 }
951 };
952 log.debug(" | target_addr = 0x{x}", .{result});
953
954 if (rel.r_length == 3) {
955 mem.writeIntLittle(u64, atom_code[rel_offset..][0..8], @as(u64, @bitCast(result)));
956 } else {
957 mem.writeIntLittle(u32, atom_code[rel_offset..][0..4], @as(u32, @truncate(@as(u64, @bitCast(result)))));
958 }
959
960 subtractor = null;
961 },
962
963 .ARM64_RELOC_ADDEND => unreachable,
964 .ARM64_RELOC_SUBTRACTOR => unreachable,
965 }
966 }
967}
968
969fn resolveRelocsX86(
970 macho_file: *MachO,
971 atom_index: Index,
972 atom_code: []u8,
973 atom_relocs: []align(1) const macho.relocation_info,
974 context: RelocContext,
975) !void {
976 const atom = macho_file.getAtom(atom_index);
977 const object = macho_file.objects.items[atom.getFile().?];
978
979 var subtractor: ?SymbolWithLoc = null;
980
981 for (atom_relocs) |rel| {
982 const rel_type = @as(macho.reloc_type_x86_64, @enumFromInt(rel.r_type));
983
984 switch (rel_type) {
985 .X86_64_RELOC_SUBTRACTOR => {
986 assert(subtractor == null);
987
988 log.debug(" RELA({s}) @ {x} => %{d} in object({?d})", .{
989 @tagName(rel_type),
990 rel.r_address,
991 rel.r_symbolnum,
992 atom.getFile(),
993 });
994
995 subtractor = parseRelocTarget(macho_file, .{
996 .object_id = atom.getFile().?,
997 .rel = rel,
998 .code = atom_code,
999 .base_addr = context.base_addr,
1000 .base_offset = context.base_offset,
1001 });
1002 continue;
1003 },
1004 else => {},
1005 }
1006
1007 const target = parseRelocTarget(macho_file, .{
1008 .object_id = atom.getFile().?,
1009 .rel = rel,
1010 .code = atom_code,
1011 .base_addr = context.base_addr,
1012 .base_offset = context.base_offset,
1013 });
1014 const rel_offset = @as(u32, @intCast(rel.r_address - context.base_offset));
1015
1016 log.debug(" RELA({s}) @ {x} => %{d} ('{s}') in object({?})", .{
1017 @tagName(rel_type),
1018 rel.r_address,
1019 target.sym_index,
1020 macho_file.getSymbolName(target),
1021 target.getFile(),
1022 });
1023
1024 const source_addr = blk: {
1025 const source_sym = macho_file.getSymbol(atom.getSymbolWithLoc());
1026 break :blk source_sym.n_value + rel_offset;
1027 };
1028 const target_addr = blk: {
1029 if (relocRequiresGot(macho_file, rel)) break :blk macho_file.getGotEntryAddress(target).?;
1030 if (relocIsStub(macho_file, rel) and macho_file.getSymbol(target).undf())
1031 break :blk macho_file.getStubsEntryAddress(target).?;
1032 if (relocIsTlv(macho_file, rel) and macho_file.getSymbol(target).undf())
1033 break :blk macho_file.getTlvPtrEntryAddress(target).?;
1034 const is_tlv = is_tlv: {
1035 const source_sym = macho_file.getSymbol(atom.getSymbolWithLoc());
1036 const header = macho_file.sections.items(.header)[source_sym.n_sect - 1];
1037 break :is_tlv header.type() == macho.S_THREAD_LOCAL_VARIABLES;
1038 };
1039 break :blk getRelocTargetAddress(macho_file, target, is_tlv);
1040 };
1041
1042 log.debug(" | source_addr = 0x{x}", .{source_addr});
1043
1044 switch (rel_type) {
1045 .X86_64_RELOC_BRANCH => {
1046 const addend = mem.readIntLittle(i32, atom_code[rel_offset..][0..4]);
1047 const adjusted_target_addr = @as(u64, @intCast(@as(i64, @intCast(target_addr)) + addend));
1048 log.debug(" | target_addr = 0x{x}", .{adjusted_target_addr});
1049 const disp = try Relocation.calcPcRelativeDisplacementX86(source_addr, adjusted_target_addr, 0);
1050 mem.writeIntLittle(i32, atom_code[rel_offset..][0..4], disp);
1051 },
1052
1053 .X86_64_RELOC_GOT,
1054 .X86_64_RELOC_GOT_LOAD,
1055 => {
1056 const addend = mem.readIntLittle(i32, atom_code[rel_offset..][0..4]);
1057 const adjusted_target_addr = @as(u64, @intCast(@as(i64, @intCast(target_addr)) + addend));
1058 log.debug(" | target_addr = 0x{x}", .{adjusted_target_addr});
1059 const disp = try Relocation.calcPcRelativeDisplacementX86(source_addr, adjusted_target_addr, 0);
1060 mem.writeIntLittle(i32, atom_code[rel_offset..][0..4], disp);
1061 },
1062
1063 .X86_64_RELOC_TLV => {
1064 const addend = mem.readIntLittle(i32, atom_code[rel_offset..][0..4]);
1065 const adjusted_target_addr = @as(u64, @intCast(@as(i64, @intCast(target_addr)) + addend));
1066 log.debug(" | target_addr = 0x{x}", .{adjusted_target_addr});
1067 const disp = try Relocation.calcPcRelativeDisplacementX86(source_addr, adjusted_target_addr, 0);
1068
1069 if (macho_file.tlv_ptr_table.lookup.get(target) == null) {
1070 // We need to rewrite the opcode from movq to leaq.
1071 atom_code[rel_offset - 2] = 0x8d;
1072 }
1073
1074 mem.writeIntLittle(i32, atom_code[rel_offset..][0..4], disp);
1075 },
1076
1077 .X86_64_RELOC_SIGNED,
1078 .X86_64_RELOC_SIGNED_1,
1079 .X86_64_RELOC_SIGNED_2,
1080 .X86_64_RELOC_SIGNED_4,
1081 => {
1082 const correction: u3 = switch (rel_type) {
1083 .X86_64_RELOC_SIGNED => 0,
1084 .X86_64_RELOC_SIGNED_1 => 1,
1085 .X86_64_RELOC_SIGNED_2 => 2,
1086 .X86_64_RELOC_SIGNED_4 => 4,
1087 else => unreachable,
1088 };
1089 var addend = mem.readIntLittle(i32, atom_code[rel_offset..][0..4]) + correction;
1090
1091 if (rel.r_extern == 0) {
1092 const base_addr = if (target.sym_index >= object.source_address_lookup.len)
1093 @as(i64, @intCast(object.getSourceSection(@as(u8, @intCast(rel.r_symbolnum - 1))).addr))
1094 else
1095 object.source_address_lookup[target.sym_index];
1096 addend += @as(i32, @intCast(@as(i64, @intCast(context.base_addr)) + rel.r_address + 4 -
1097 @as(i64, @intCast(base_addr))));
1098 }
1099
1100 const adjusted_target_addr = @as(u64, @intCast(@as(i64, @intCast(target_addr)) + addend));
1101
1102 log.debug(" | target_addr = 0x{x}", .{adjusted_target_addr});
1103
1104 const disp = try Relocation.calcPcRelativeDisplacementX86(source_addr, adjusted_target_addr, correction);
1105 mem.writeIntLittle(i32, atom_code[rel_offset..][0..4], disp);
1106 },
1107
1108 .X86_64_RELOC_UNSIGNED => {
1109 var addend = if (rel.r_length == 3)
1110 mem.readIntLittle(i64, atom_code[rel_offset..][0..8])
1111 else
1112 mem.readIntLittle(i32, atom_code[rel_offset..][0..4]);
1113
1114 if (rel.r_extern == 0) {
1115 const base_addr = if (target.sym_index >= object.source_address_lookup.len)
1116 @as(i64, @intCast(object.getSourceSection(@as(u8, @intCast(rel.r_symbolnum - 1))).addr))
1117 else
1118 object.source_address_lookup[target.sym_index];
1119 addend -= base_addr;
1120 }
1121
1122 const result = blk: {
1123 if (subtractor) |sub| {
1124 const sym = macho_file.getSymbol(sub);
1125 break :blk @as(i64, @intCast(target_addr)) - @as(i64, @intCast(sym.n_value)) + addend;
1126 } else {
1127 break :blk @as(i64, @intCast(target_addr)) + addend;
1128 }
1129 };
1130 log.debug(" | target_addr = 0x{x}", .{result});
1131
1132 if (rel.r_length == 3) {
1133 mem.writeIntLittle(u64, atom_code[rel_offset..][0..8], @as(u64, @bitCast(result)));
1134 } else {
1135 mem.writeIntLittle(u32, atom_code[rel_offset..][0..4], @as(u32, @truncate(@as(u64, @bitCast(result)))));
1136 }
1137
1138 subtractor = null;
1139 },
1140
1141 .X86_64_RELOC_SUBTRACTOR => unreachable,
1142 }
1143 }
1144}
1145
1146pub fn getAtomCode(macho_file: *MachO, atom_index: Index) []const u8 {
1147 const atom = macho_file.getAtom(atom_index);
1148 assert(atom.getFile() != null); // Synthetic atom shouldn't need to inquire for code.
1149 const object = macho_file.objects.items[atom.getFile().?];
1150 const source_sym = object.getSourceSymbol(atom.sym_index) orelse {
1151 // If there was no matching symbol present in the source symtab, this means
1152 // we are dealing with either an entire section, or part of it, but also
1153 // starting at the beginning.
1154 const nbase = @as(u32, @intCast(object.in_symtab.?.len));
1155 const sect_id = @as(u8, @intCast(atom.sym_index - nbase));
1156 const source_sect = object.getSourceSection(sect_id);
1157 assert(!source_sect.isZerofill());
1158 const code = object.getSectionContents(source_sect);
1159 const code_len = @as(usize, @intCast(atom.size));
1160 return code[0..code_len];
1161 };
1162 const source_sect = object.getSourceSection(source_sym.n_sect - 1);
1163 assert(!source_sect.isZerofill());
1164 const code = object.getSectionContents(source_sect);
1165 const offset = @as(usize, @intCast(source_sym.n_value - source_sect.addr));
1166 const code_len = @as(usize, @intCast(atom.size));
1167 return code[offset..][0..code_len];
1168}
1169
1170pub fn getAtomRelocs(macho_file: *MachO, atom_index: Index) []const macho.relocation_info {
1171 const atom = macho_file.getAtom(atom_index);
1172 assert(atom.getFile() != null); // Synthetic atom shouldn't need to unique for relocs.
1173 const object = macho_file.objects.items[atom.getFile().?];
1174 const cache = object.relocs_lookup[atom.sym_index];
1175
1176 const source_sect_id = if (object.getSourceSymbol(atom.sym_index)) |source_sym| blk: {
1177 break :blk source_sym.n_sect - 1;
1178 } else blk: {
1179 // If there was no matching symbol present in the source symtab, this means
1180 // we are dealing with either an entire section, or part of it, but also
1181 // starting at the beginning.
1182 const nbase = @as(u32, @intCast(object.in_symtab.?.len));
1183 const sect_id = @as(u8, @intCast(atom.sym_index - nbase));
1184 break :blk sect_id;
1185 };
1186 const source_sect = object.getSourceSection(source_sect_id);
1187 assert(!source_sect.isZerofill());
1188 const relocs = object.getRelocs(source_sect_id);
1189 return relocs[cache.start..][0..cache.len];
1190}
1191
1192pub fn relocRequiresGot(macho_file: *MachO, rel: macho.relocation_info) bool {
1193 switch (macho_file.base.options.target.cpu.arch) {
1194 .aarch64 => switch (@as(macho.reloc_type_arm64, @enumFromInt(rel.r_type))) {
1195 .ARM64_RELOC_GOT_LOAD_PAGE21,
1196 .ARM64_RELOC_GOT_LOAD_PAGEOFF12,
1197 .ARM64_RELOC_POINTER_TO_GOT,
1198 => return true,
1199 else => return false,
1200 },
1201 .x86_64 => switch (@as(macho.reloc_type_x86_64, @enumFromInt(rel.r_type))) {
1202 .X86_64_RELOC_GOT,
1203 .X86_64_RELOC_GOT_LOAD,
1204 => return true,
1205 else => return false,
1206 },
1207 else => unreachable,
1208 }
1209}
1210
1211pub fn relocIsTlv(macho_file: *MachO, rel: macho.relocation_info) bool {
1212 switch (macho_file.base.options.target.cpu.arch) {
1213 .aarch64 => switch (@as(macho.reloc_type_arm64, @enumFromInt(rel.r_type))) {
1214 .ARM64_RELOC_TLVP_LOAD_PAGE21,
1215 .ARM64_RELOC_TLVP_LOAD_PAGEOFF12,
1216 => return true,
1217 else => return false,
1218 },
1219 .x86_64 => switch (@as(macho.reloc_type_x86_64, @enumFromInt(rel.r_type))) {
1220 .X86_64_RELOC_TLV => return true,
1221 else => return false,
1222 },
1223 else => unreachable,
1224 }
1225}
1226
1227pub fn relocIsStub(macho_file: *MachO, rel: macho.relocation_info) bool {
1228 switch (macho_file.base.options.target.cpu.arch) {
1229 .aarch64 => switch (@as(macho.reloc_type_arm64, @enumFromInt(rel.r_type))) {
1230 .ARM64_RELOC_BRANCH26 => return true,
1231 else => return false,
1232 },
1233 .x86_64 => switch (@as(macho.reloc_type_x86_64, @enumFromInt(rel.r_type))) {
1234 .X86_64_RELOC_BRANCH => return true,
1235 else => return false,
1236 },
1237 else => unreachable,
1238 }
1239}
1240
1241const Atom = @This();
1242
1243const std = @import("std");
1244const build_options = @import("build_options");
1245const aarch64 = @import("../../arch/aarch64/bits.zig");
1246const assert = std.debug.assert;
1247const log = std.log.scoped(.link);
1248const macho = std.macho;
1249const math = std.math;
1250const mem = std.mem;
1251const meta = std.meta;
1252const trace = @import("../../tracy.zig").trace;
1253
1254const Allocator = mem.Allocator;
1255const Arch = std.Target.Cpu.Arch;
1256const MachO = @import("../MachO.zig");
1257pub const Relocation = @import("Relocation.zig");
1258const SymbolWithLoc = MachO.SymbolWithLoc;
src/link/MachO/CodeSignature.zig+182-182
......@@ -1,17 +1,175 @@
1const CodeSignature = @This();
1page_size: u16,
2code_directory: CodeDirectory,
3requirements: ?Requirements = null,
4entitlements: ?Entitlements = null,
5signature: ?Signature = null,
26
3const std = @import("std");
4const assert = std.debug.assert;
5const fs = std.fs;
6const log = std.log.scoped(.link);
7const macho = std.macho;
8const mem = std.mem;
9const testing = std.testing;
7pub fn init(page_size: u16) CodeSignature {
8 return .{
9 .page_size = page_size,
10 .code_directory = CodeDirectory.init(page_size),
11 };
12}
1013
11const Allocator = mem.Allocator;
12const Compilation = @import("../../Compilation.zig");
13const Hasher = @import("hasher.zig").ParallelHasher;
14const Sha256 = std.crypto.hash.sha2.Sha256;
14pub fn deinit(self: *CodeSignature, allocator: Allocator) void {
15 self.code_directory.deinit(allocator);
16 if (self.requirements) |*req| {
17 req.deinit(allocator);
18 }
19 if (self.entitlements) |*ents| {
20 ents.deinit(allocator);
21 }
22 if (self.signature) |*sig| {
23 sig.deinit(allocator);
24 }
25}
26
27pub fn addEntitlements(self: *CodeSignature, allocator: Allocator, path: []const u8) !void {
28 const file = try fs.cwd().openFile(path, .{});
29 defer file.close();
30 const inner = try file.readToEndAlloc(allocator, std.math.maxInt(u32));
31 self.entitlements = .{ .inner = inner };
32}
33
34pub const WriteOpts = struct {
35 file: fs.File,
36 exec_seg_base: u64,
37 exec_seg_limit: u64,
38 file_size: u32,
39 output_mode: std.builtin.OutputMode,
40};
41
42pub fn writeAdhocSignature(
43 self: *CodeSignature,
44 comp: *const Compilation,
45 opts: WriteOpts,
46 writer: anytype,
47) !void {
48 const gpa = comp.gpa;
49
50 var header: macho.SuperBlob = .{
51 .magic = macho.CSMAGIC_EMBEDDED_SIGNATURE,
52 .length = @sizeOf(macho.SuperBlob),
53 .count = 0,
54 };
55
56 var blobs = std.ArrayList(Blob).init(gpa);
57 defer blobs.deinit();
58
59 self.code_directory.inner.execSegBase = opts.exec_seg_base;
60 self.code_directory.inner.execSegLimit = opts.exec_seg_limit;
61 self.code_directory.inner.execSegFlags = if (opts.output_mode == .Exe) macho.CS_EXECSEG_MAIN_BINARY else 0;
62 self.code_directory.inner.codeLimit = opts.file_size;
63
64 const total_pages = @as(u32, @intCast(mem.alignForward(usize, opts.file_size, self.page_size) / self.page_size));
65
66 try self.code_directory.code_slots.ensureTotalCapacityPrecise(gpa, total_pages);
67 self.code_directory.code_slots.items.len = total_pages;
68 self.code_directory.inner.nCodeSlots = total_pages;
69
70 // Calculate hash for each page (in file) and write it to the buffer
71 var hasher = Hasher(Sha256){ .allocator = gpa, .thread_pool = comp.thread_pool };
72 try hasher.hash(opts.file, self.code_directory.code_slots.items, .{
73 .chunk_size = self.page_size,
74 .max_file_size = opts.file_size,
75 });
76
77 try blobs.append(.{ .code_directory = &self.code_directory });
78 header.length += @sizeOf(macho.BlobIndex);
79 header.count += 1;
80
81 var hash: [hash_size]u8 = undefined;
82
83 if (self.requirements) |*req| {
84 var buf = std.ArrayList(u8).init(gpa);
85 defer buf.deinit();
86 try req.write(buf.writer());
87 Sha256.hash(buf.items, &hash, .{});
88 self.code_directory.addSpecialHash(req.slotType(), hash);
89
90 try blobs.append(.{ .requirements = req });
91 header.count += 1;
92 header.length += @sizeOf(macho.BlobIndex) + req.size();
93 }
94
95 if (self.entitlements) |*ents| {
96 var buf = std.ArrayList(u8).init(gpa);
97 defer buf.deinit();
98 try ents.write(buf.writer());
99 Sha256.hash(buf.items, &hash, .{});
100 self.code_directory.addSpecialHash(ents.slotType(), hash);
101
102 try blobs.append(.{ .entitlements = ents });
103 header.count += 1;
104 header.length += @sizeOf(macho.BlobIndex) + ents.size();
105 }
106
107 if (self.signature) |*sig| {
108 try blobs.append(.{ .signature = sig });
109 header.count += 1;
110 header.length += @sizeOf(macho.BlobIndex) + sig.size();
111 }
112
113 self.code_directory.inner.hashOffset =
114 @sizeOf(macho.CodeDirectory) + @as(u32, @intCast(self.code_directory.ident.len + 1 + self.code_directory.inner.nSpecialSlots * hash_size));
115 self.code_directory.inner.length = self.code_directory.size();
116 header.length += self.code_directory.size();
117
118 try writer.writeIntBig(u32, header.magic);
119 try writer.writeIntBig(u32, header.length);
120 try writer.writeIntBig(u32, header.count);
121
122 var offset: u32 = @sizeOf(macho.SuperBlob) + @sizeOf(macho.BlobIndex) * @as(u32, @intCast(blobs.items.len));
123 for (blobs.items) |blob| {
124 try writer.writeIntBig(u32, blob.slotType());
125 try writer.writeIntBig(u32, offset);
126 offset += blob.size();
127 }
128
129 for (blobs.items) |blob| {
130 try blob.write(writer);
131 }
132}
133
134pub fn size(self: CodeSignature) u32 {
135 var ssize: u32 = @sizeOf(macho.SuperBlob) + @sizeOf(macho.BlobIndex) + self.code_directory.size();
136 if (self.requirements) |req| {
137 ssize += @sizeOf(macho.BlobIndex) + req.size();
138 }
139 if (self.entitlements) |ent| {
140 ssize += @sizeOf(macho.BlobIndex) + ent.size();
141 }
142 if (self.signature) |sig| {
143 ssize += @sizeOf(macho.BlobIndex) + sig.size();
144 }
145 return ssize;
146}
147
148pub fn estimateSize(self: CodeSignature, file_size: u64) u32 {
149 var ssize: u64 = @sizeOf(macho.SuperBlob) + @sizeOf(macho.BlobIndex) + self.code_directory.size();
150 // Approx code slots
151 const total_pages = mem.alignForward(u64, file_size, self.page_size) / self.page_size;
152 ssize += total_pages * hash_size;
153 var n_special_slots: u32 = 0;
154 if (self.requirements) |req| {
155 ssize += @sizeOf(macho.BlobIndex) + req.size();
156 n_special_slots = @max(n_special_slots, req.slotType());
157 }
158 if (self.entitlements) |ent| {
159 ssize += @sizeOf(macho.BlobIndex) + ent.size() + hash_size;
160 n_special_slots = @max(n_special_slots, ent.slotType());
161 }
162 if (self.signature) |sig| {
163 ssize += @sizeOf(macho.BlobIndex) + sig.size();
164 }
165 ssize += n_special_slots * hash_size;
166 return @as(u32, @intCast(mem.alignForward(u64, ssize, @sizeOf(u64))));
167}
168
169pub fn clear(self: *CodeSignature, allocator: Allocator) void {
170 self.code_directory.deinit(allocator);
171 self.code_directory = CodeDirectory.init(self.page_size);
172}
15173
16174const hash_size = Sha256.digest_length;
17175
......@@ -218,175 +376,17 @@ const Signature = struct {
218376 }
219377};
220378
221page_size: u16,
222code_directory: CodeDirectory,
223requirements: ?Requirements = null,
224entitlements: ?Entitlements = null,
225signature: ?Signature = null,
226
227pub fn init(page_size: u16) CodeSignature {
228 return .{
229 .page_size = page_size,
230 .code_directory = CodeDirectory.init(page_size),
231 };
232}
233
234pub fn deinit(self: *CodeSignature, allocator: Allocator) void {
235 self.code_directory.deinit(allocator);
236 if (self.requirements) |*req| {
237 req.deinit(allocator);
238 }
239 if (self.entitlements) |*ents| {
240 ents.deinit(allocator);
241 }
242 if (self.signature) |*sig| {
243 sig.deinit(allocator);
244 }
245}
246
247pub fn addEntitlements(self: *CodeSignature, allocator: Allocator, path: []const u8) !void {
248 const file = try fs.cwd().openFile(path, .{});
249 defer file.close();
250 const inner = try file.readToEndAlloc(allocator, std.math.maxInt(u32));
251 self.entitlements = .{ .inner = inner };
252}
253
254pub const WriteOpts = struct {
255 file: fs.File,
256 exec_seg_base: u64,
257 exec_seg_limit: u64,
258 file_size: u32,
259 output_mode: std.builtin.OutputMode,
260};
261
262pub fn writeAdhocSignature(
263 self: *CodeSignature,
264 comp: *const Compilation,
265 opts: WriteOpts,
266 writer: anytype,
267) !void {
268 const gpa = comp.gpa;
269
270 var header: macho.SuperBlob = .{
271 .magic = macho.CSMAGIC_EMBEDDED_SIGNATURE,
272 .length = @sizeOf(macho.SuperBlob),
273 .count = 0,
274 };
275
276 var blobs = std.ArrayList(Blob).init(gpa);
277 defer blobs.deinit();
278
279 self.code_directory.inner.execSegBase = opts.exec_seg_base;
280 self.code_directory.inner.execSegLimit = opts.exec_seg_limit;
281 self.code_directory.inner.execSegFlags = if (opts.output_mode == .Exe) macho.CS_EXECSEG_MAIN_BINARY else 0;
282 self.code_directory.inner.codeLimit = opts.file_size;
283
284 const total_pages = @as(u32, @intCast(mem.alignForward(usize, opts.file_size, self.page_size) / self.page_size));
285
286 try self.code_directory.code_slots.ensureTotalCapacityPrecise(gpa, total_pages);
287 self.code_directory.code_slots.items.len = total_pages;
288 self.code_directory.inner.nCodeSlots = total_pages;
289
290 // Calculate hash for each page (in file) and write it to the buffer
291 var hasher = Hasher(Sha256){ .allocator = gpa, .thread_pool = comp.thread_pool };
292 try hasher.hash(opts.file, self.code_directory.code_slots.items, .{
293 .chunk_size = self.page_size,
294 .max_file_size = opts.file_size,
295 });
296
297 try blobs.append(.{ .code_directory = &self.code_directory });
298 header.length += @sizeOf(macho.BlobIndex);
299 header.count += 1;
300
301 var hash: [hash_size]u8 = undefined;
302
303 if (self.requirements) |*req| {
304 var buf = std.ArrayList(u8).init(gpa);
305 defer buf.deinit();
306 try req.write(buf.writer());
307 Sha256.hash(buf.items, &hash, .{});
308 self.code_directory.addSpecialHash(req.slotType(), hash);
309
310 try blobs.append(.{ .requirements = req });
311 header.count += 1;
312 header.length += @sizeOf(macho.BlobIndex) + req.size();
313 }
314
315 if (self.entitlements) |*ents| {
316 var buf = std.ArrayList(u8).init(gpa);
317 defer buf.deinit();
318 try ents.write(buf.writer());
319 Sha256.hash(buf.items, &hash, .{});
320 self.code_directory.addSpecialHash(ents.slotType(), hash);
321
322 try blobs.append(.{ .entitlements = ents });
323 header.count += 1;
324 header.length += @sizeOf(macho.BlobIndex) + ents.size();
325 }
326
327 if (self.signature) |*sig| {
328 try blobs.append(.{ .signature = sig });
329 header.count += 1;
330 header.length += @sizeOf(macho.BlobIndex) + sig.size();
331 }
332
333 self.code_directory.inner.hashOffset =
334 @sizeOf(macho.CodeDirectory) + @as(u32, @intCast(self.code_directory.ident.len + 1 + self.code_directory.inner.nSpecialSlots * hash_size));
335 self.code_directory.inner.length = self.code_directory.size();
336 header.length += self.code_directory.size();
337
338 try writer.writeIntBig(u32, header.magic);
339 try writer.writeIntBig(u32, header.length);
340 try writer.writeIntBig(u32, header.count);
341
342 var offset: u32 = @sizeOf(macho.SuperBlob) + @sizeOf(macho.BlobIndex) * @as(u32, @intCast(blobs.items.len));
343 for (blobs.items) |blob| {
344 try writer.writeIntBig(u32, blob.slotType());
345 try writer.writeIntBig(u32, offset);
346 offset += blob.size();
347 }
348
349 for (blobs.items) |blob| {
350 try blob.write(writer);
351 }
352}
353
354pub fn size(self: CodeSignature) u32 {
355 var ssize: u32 = @sizeOf(macho.SuperBlob) + @sizeOf(macho.BlobIndex) + self.code_directory.size();
356 if (self.requirements) |req| {
357 ssize += @sizeOf(macho.BlobIndex) + req.size();
358 }
359 if (self.entitlements) |ent| {
360 ssize += @sizeOf(macho.BlobIndex) + ent.size();
361 }
362 if (self.signature) |sig| {
363 ssize += @sizeOf(macho.BlobIndex) + sig.size();
364 }
365 return ssize;
366}
379const CodeSignature = @This();
367380
368pub fn estimateSize(self: CodeSignature, file_size: u64) u32 {
369 var ssize: u64 = @sizeOf(macho.SuperBlob) + @sizeOf(macho.BlobIndex) + self.code_directory.size();
370 // Approx code slots
371 const total_pages = mem.alignForward(u64, file_size, self.page_size) / self.page_size;
372 ssize += total_pages * hash_size;
373 var n_special_slots: u32 = 0;
374 if (self.requirements) |req| {
375 ssize += @sizeOf(macho.BlobIndex) + req.size();
376 n_special_slots = @max(n_special_slots, req.slotType());
377 }
378 if (self.entitlements) |ent| {
379 ssize += @sizeOf(macho.BlobIndex) + ent.size() + hash_size;
380 n_special_slots = @max(n_special_slots, ent.slotType());
381 }
382 if (self.signature) |sig| {
383 ssize += @sizeOf(macho.BlobIndex) + sig.size();
384 }
385 ssize += n_special_slots * hash_size;
386 return @as(u32, @intCast(mem.alignForward(u64, ssize, @sizeOf(u64))));
387}
381const std = @import("std");
382const assert = std.debug.assert;
383const fs = std.fs;
384const log = std.log.scoped(.link);
385const macho = std.macho;
386const mem = std.mem;
387const testing = std.testing;
388388
389pub fn clear(self: *CodeSignature, allocator: Allocator) void {
390 self.code_directory.deinit(allocator);
391 self.code_directory = CodeDirectory.init(self.page_size);
392}
389const Allocator = mem.Allocator;
390const Compilation = @import("../../Compilation.zig");
391const Hasher = @import("hasher.zig").ParallelHasher;
392const Sha256 = std.crypto.hash.sha2.Sha256;
src/link/MachO/DebugSymbols.zig+34-32
......@@ -1,30 +1,6 @@
1const DebugSymbols = @This();
2
3const std = @import("std");
4const build_options = @import("build_options");
5const assert = std.debug.assert;
6const fs = std.fs;
7const link = @import("../../link.zig");
8const load_commands = @import("load_commands.zig");
9const log = std.log.scoped(.dsym);
10const macho = std.macho;
11const makeStaticString = MachO.makeStaticString;
12const math = std.math;
13const mem = std.mem;
14const padToIdeal = MachO.padToIdeal;
15const trace = @import("../../tracy.zig").trace;
16
17const Allocator = mem.Allocator;
18const Dwarf = @import("../Dwarf.zig");
19const MachO = @import("../MachO.zig");
20const Module = @import("../../Module.zig");
21const StringTable = @import("../strtab.zig").StringTable;
22const Type = @import("../../type.zig").Type;
23
241allocator: Allocator,
252dwarf: Dwarf,
263file: fs.File,
27page_size: u16,
284
295symtab_cmd: macho.symtab_command = .{},
306
......@@ -62,13 +38,14 @@ pub const Reloc = struct {
6238
6339/// You must call this function *after* `MachO.populateMissingMetadata()`
6440/// has been called to get a viable debug symbols output.
65pub fn populateMissingMetadata(self: *DebugSymbols) !void {
41pub fn populateMissingMetadata(self: *DebugSymbols, macho_file: *MachO) !void {
6642 if (self.dwarf_segment_cmd_index == null) {
6743 self.dwarf_segment_cmd_index = @as(u8, @intCast(self.segments.items.len));
6844
69 const off = @as(u64, @intCast(self.page_size));
45 const page_size = MachO.getPageSize(macho_file.base.options.target.cpu.arch);
46 const off = @as(u64, @intCast(page_size));
7047 const ideal_size: u16 = 200 + 128 + 160 + 250;
71 const needed_size = mem.alignForward(u64, padToIdeal(ideal_size), self.page_size);
48 const needed_size = mem.alignForward(u64, padToIdeal(ideal_size), page_size);
7249
7350 log.debug("found __DWARF segment free space 0x{x} to 0x{x}", .{ off, off + needed_size });
7451
......@@ -355,7 +332,8 @@ fn finalizeDwarfSegment(self: *DebugSymbols, macho_file: *MachO) void {
355332 file_size = @max(file_size, header.offset + header.size);
356333 }
357334
358 const aligned_size = mem.alignForward(u64, file_size, self.page_size);
335 const page_size = MachO.getPageSize(macho_file.base.options.target.cpu.arch);
336 const aligned_size = mem.alignForward(u64, file_size, page_size);
359337 dwarf_segment.vmaddr = base_vmaddr;
360338 dwarf_segment.filesize = aligned_size;
361339 dwarf_segment.vmsize = aligned_size;
......@@ -364,12 +342,12 @@ fn finalizeDwarfSegment(self: *DebugSymbols, macho_file: *MachO) void {
364342 linkedit.vmaddr = mem.alignForward(
365343 u64,
366344 dwarf_segment.vmaddr + aligned_size,
367 self.page_size,
345 page_size,
368346 );
369347 linkedit.fileoff = mem.alignForward(
370348 u64,
371349 dwarf_segment.fileoff + aligned_size,
372 self.page_size,
350 page_size,
373351 );
374352 log.debug("found __LINKEDIT segment free space at 0x{x}", .{linkedit.fileoff});
375353}
......@@ -457,8 +435,9 @@ fn writeLinkeditSegmentData(self: *DebugSymbols, macho_file: *MachO) !void {
457435 try self.writeSymtab(macho_file);
458436 try self.writeStrtab();
459437
438 const page_size = MachO.getPageSize(macho_file.base.options.target.cpu.arch);
460439 const seg = &self.segments.items[self.linkedit_segment_cmd_index.?];
461 const aligned_size = mem.alignForward(u64, seg.filesize, self.page_size);
440 const aligned_size = mem.alignForward(u64, seg.filesize, page_size);
462441 seg.vmsize = aligned_size;
463442}
464443
......@@ -473,7 +452,7 @@ fn writeSymtab(self: *DebugSymbols, macho_file: *MachO) !void {
473452
474453 for (macho_file.locals.items, 0..) |sym, sym_id| {
475454 if (sym.n_strx == 0) continue; // no name, skip
476 const sym_loc = MachO.SymbolWithLoc{ .sym_index = @as(u32, @intCast(sym_id)), .file = null };
455 const sym_loc = MachO.SymbolWithLoc{ .sym_index = @as(u32, @intCast(sym_id)) };
477456 if (macho_file.symbolIsTemp(sym_loc)) continue; // local temp symbol, skip
478457 if (macho_file.getGlobal(macho_file.getSymbolName(sym_loc)) != null) continue; // global symbol is either an export or import, skip
479458 var out_sym = sym;
......@@ -567,3 +546,26 @@ pub fn getSection(self: DebugSymbols, sect: u8) macho.section_64 {
567546 assert(sect < self.sections.items.len);
568547 return self.sections.items[sect];
569548}
549
550const DebugSymbols = @This();
551
552const std = @import("std");
553const build_options = @import("build_options");
554const assert = std.debug.assert;
555const fs = std.fs;
556const link = @import("../../link.zig");
557const load_commands = @import("load_commands.zig");
558const log = std.log.scoped(.dsym);
559const macho = std.macho;
560const makeStaticString = MachO.makeStaticString;
561const math = std.math;
562const mem = std.mem;
563const padToIdeal = MachO.padToIdeal;
564const trace = @import("../../tracy.zig").trace;
565
566const Allocator = mem.Allocator;
567const Dwarf = @import("../Dwarf.zig");
568const MachO = @import("../MachO.zig");
569const Module = @import("../../Module.zig");
570const StringTable = @import("../strtab.zig").StringTable;
571const Type = @import("../../type.zig").Type;
src/link/MachO/DwarfInfo.zig+16-15
......@@ -1,17 +1,3 @@
1const DwarfInfo = @This();
2
3const std = @import("std");
4const assert = std.debug.assert;
5const dwarf = std.dwarf;
6const leb = std.leb;
7const log = std.log.scoped(.macho);
8const math = std.math;
9const mem = std.mem;
10
11const Allocator = mem.Allocator;
12pub const AbbrevLookupTable = std.AutoHashMap(u64, struct { pos: usize, len: usize });
13pub const SubprogramLookupByName = std.StringHashMap(struct { addr: u64, size: u64 });
14
151debug_info: []const u8,
162debug_abbrev: []const u8,
173debug_str: []const u8,
......@@ -458,7 +444,8 @@ fn findFormSize(self: DwarfInfo, form: u64, di_off: usize, cuh: CompileUnit.Head
458444 },
459445
460446 else => {
461 log.err("unhandled DW_FORM_* value with identifier {x}", .{form});
447 // TODO figure out how to handle this
448 log.debug("unhandled DW_FORM_* value with identifier {x}", .{form});
462449 return error.UnhandledDwFormValue;
463450 },
464451 }
......@@ -501,3 +488,17 @@ fn getString(self: DwarfInfo, off: u64) []const u8 {
501488 assert(off < self.debug_str.len);
502489 return mem.sliceTo(@as([*:0]const u8, @ptrCast(self.debug_str.ptr + @as(usize, @intCast(off)))), 0);
503490}
491
492const DwarfInfo = @This();
493
494const std = @import("std");
495const assert = std.debug.assert;
496const dwarf = std.dwarf;
497const leb = std.leb;
498const log = std.log.scoped(.macho);
499const math = std.math;
500const mem = std.mem;
501
502const Allocator = mem.Allocator;
503pub const AbbrevLookupTable = std.AutoHashMap(u64, struct { pos: usize, len: usize });
504pub const SubprogramLookupByName = std.StringHashMap(struct { addr: u64, size: u64 });
src/link/MachO/Dylib.zig+90-89
......@@ -1,25 +1,8 @@
1const Dylib = @This();
2
3const std = @import("std");
4const assert = std.debug.assert;
5const fs = std.fs;
6const fmt = std.fmt;
7const log = std.log.scoped(.link);
8const macho = std.macho;
9const math = std.math;
10const mem = std.mem;
11const fat = @import("fat.zig");
12const tapi = @import("../tapi.zig");
13
14const Allocator = mem.Allocator;
15const CrossTarget = std.zig.CrossTarget;
16const LibStub = tapi.LibStub;
17const LoadCommandIterator = macho.LoadCommandIterator;
18const MachO = @import("../MachO.zig");
19const Tbd = tapi.Tbd;
20
1path: []const u8,
212id: ?Id = null,
223weak: bool = false,
4/// Header is only set if Dylib is parsed directly from a binary and not a stub file.
5header: ?macho.mach_header_64 = null,
236
247/// Parsed symbol table represented as hash map of symbols'
258/// names. We can and should defer creating *Symbols until
......@@ -116,7 +99,15 @@ pub const Id = struct {
11699 }
117100};
118101
102pub fn isDylib(file: std.fs.File, fat_offset: u64) bool {
103 const reader = file.reader();
104 const hdr = reader.readStruct(macho.mach_header_64) catch return false;
105 defer file.seekTo(fat_offset) catch {};
106 return hdr.filetype == macho.MH_DYLIB;
107}
108
119109pub fn deinit(self: *Dylib, allocator: Allocator) void {
110 allocator.free(self.path);
120111 for (self.symbols.keys()) |key| {
121112 allocator.free(key);
122113 }
......@@ -129,7 +120,6 @@ pub fn deinit(self: *Dylib, allocator: Allocator) void {
129120pub fn parseFromBinary(
130121 self: *Dylib,
131122 allocator: Allocator,
132 cpu_arch: std.Target.Cpu.Arch,
133123 dylib_id: u16,
134124 dependent_libs: anytype,
135125 name: []const u8,
......@@ -140,27 +130,12 @@ pub fn parseFromBinary(
140130
141131 log.debug("parsing shared library '{s}'", .{name});
142132
143 const header = try reader.readStruct(macho.mach_header_64);
144
145 if (header.filetype != macho.MH_DYLIB) {
146 log.debug("invalid filetype: expected 0x{x}, found 0x{x}", .{ macho.MH_DYLIB, header.filetype });
147 return error.NotDylib;
148 }
149
150 const this_arch: std.Target.Cpu.Arch = try fat.decodeArch(header.cputype, true);
151
152 if (this_arch != cpu_arch) {
153 log.err("mismatched cpu architecture: expected {s}, found {s}", .{
154 @tagName(cpu_arch),
155 @tagName(this_arch),
156 });
157 return error.MismatchedCpuArchitecture;
158 }
133 self.header = try reader.readStruct(macho.mach_header_64);
159134
160 const should_lookup_reexports = header.flags & macho.MH_NO_REEXPORTED_DYLIBS == 0;
135 const should_lookup_reexports = self.header.?.flags & macho.MH_NO_REEXPORTED_DYLIBS == 0;
161136 var it = LoadCommandIterator{
162 .ncmds = header.ncmds,
163 .buffer = data[@sizeOf(macho.mach_header_64)..][0..header.sizeofcmds],
137 .ncmds = self.header.?.ncmds,
138 .buffer = data[@sizeOf(macho.mach_header_64)..][0..self.header.?.sizeofcmds],
164139 };
165140 while (it.next()) |cmd| {
166141 switch (cmd.cmd()) {
......@@ -205,6 +180,26 @@ pub fn parseFromBinary(
205180 }
206181}
207182
183/// Returns Platform composed from the first encountered build version type load command:
184/// either LC_BUILD_VERSION or LC_VERSION_MIN_*.
185pub fn getPlatform(self: Dylib, data: []align(@alignOf(u64)) const u8) ?Platform {
186 var it = LoadCommandIterator{
187 .ncmds = self.header.?.ncmds,
188 .buffer = data[@sizeOf(macho.mach_header_64)..][0..self.header.?.sizeofcmds],
189 };
190 while (it.next()) |cmd| {
191 switch (cmd.cmd()) {
192 .BUILD_VERSION,
193 .VERSION_MIN_MACOSX,
194 .VERSION_MIN_IPHONEOS,
195 .VERSION_MIN_TVOS,
196 .VERSION_MIN_WATCHOS,
197 => return Platform.fromLoadCommand(cmd),
198 else => {},
199 }
200 } else return null;
201}
202
208203fn addObjCClassSymbol(self: *Dylib, allocator: Allocator, sym_name: []const u8) !void {
209204 const expanded = &[_][]const u8{
210205 try std.fmt.allocPrint(allocator, "_OBJC_CLASS_$_{s}", .{sym_name}),
......@@ -239,41 +234,41 @@ fn addWeakSymbol(self: *Dylib, allocator: Allocator, sym_name: []const u8) !void
239234 try self.symbols.putNoClobber(allocator, try allocator.dupe(u8, sym_name), true);
240235}
241236
242const TargetMatcher = struct {
237pub const TargetMatcher = struct {
243238 allocator: Allocator,
244 target: CrossTarget,
239 cpu_arch: std.Target.Cpu.Arch,
240 os_tag: std.Target.Os.Tag,
241 abi: std.Target.Abi,
245242 target_strings: std.ArrayListUnmanaged([]const u8) = .{},
246243
247 fn init(allocator: Allocator, target: CrossTarget) !TargetMatcher {
244 pub fn init(allocator: Allocator, target: std.Target) !TargetMatcher {
248245 var self = TargetMatcher{
249246 .allocator = allocator,
250 .target = target,
247 .cpu_arch = target.cpu.arch,
248 .os_tag = target.os.tag,
249 .abi = target.abi,
251250 };
252 const apple_string = try targetToAppleString(allocator, target);
251 const apple_string = try toAppleTargetTriple(allocator, self.cpu_arch, self.os_tag, self.abi);
253252 try self.target_strings.append(allocator, apple_string);
254253
255 const abi = target.abi orelse .none;
256 if (abi == .simulator) {
254 if (self.abi == .simulator) {
257255 // For Apple simulator targets, linking gets tricky as we need to link against the simulator
258256 // hosts dylibs too.
259 const host_target = try targetToAppleString(allocator, .{
260 .cpu_arch = target.cpu_arch.?,
261 .os_tag = .macos,
262 });
257 const host_target = try toAppleTargetTriple(allocator, self.cpu_arch, .macos, .none);
263258 try self.target_strings.append(allocator, host_target);
264259 }
265260
266261 return self;
267262 }
268263
269 fn deinit(self: *TargetMatcher) void {
264 pub fn deinit(self: *TargetMatcher) void {
270265 for (self.target_strings.items) |t| {
271266 self.allocator.free(t);
272267 }
273268 self.target_strings.deinit(self.allocator);
274269 }
275270
276 inline fn cpuArchToAppleString(cpu_arch: std.Target.Cpu.Arch) []const u8 {
271 inline fn fmtCpuArch(cpu_arch: std.Target.Cpu.Arch) []const u8 {
277272 return switch (cpu_arch) {
278273 .aarch64 => "arm64",
279274 .x86_64 => "x86_64",
......@@ -281,7 +276,7 @@ const TargetMatcher = struct {
281276 };
282277 }
283278
284 inline fn abiToAppleString(abi: std.Target.Abi) ?[]const u8 {
279 inline fn fmtAbi(abi: std.Target.Abi) ?[]const u8 {
285280 return switch (abi) {
286281 .none => null,
287282 .simulator => "simulator",
......@@ -290,14 +285,18 @@ const TargetMatcher = struct {
290285 };
291286 }
292287
293 fn targetToAppleString(allocator: Allocator, target: CrossTarget) ![]const u8 {
294 const cpu_arch = cpuArchToAppleString(target.cpu_arch.?);
295 const os_tag = @tagName(target.os_tag.?);
296 const target_abi = abiToAppleString(target.abi orelse .none);
297 if (target_abi) |abi| {
298 return std.fmt.allocPrint(allocator, "{s}-{s}-{s}", .{ cpu_arch, os_tag, abi });
288 pub fn toAppleTargetTriple(
289 allocator: Allocator,
290 cpu_arch: std.Target.Cpu.Arch,
291 os_tag: std.Target.Os.Tag,
292 abi: std.Target.Abi,
293 ) ![]const u8 {
294 const cpu_arch_s = fmtCpuArch(cpu_arch);
295 const os_tag_s = @tagName(os_tag);
296 if (fmtAbi(abi)) |abi_s| {
297 return std.fmt.allocPrint(allocator, "{s}-{s}-{s}", .{ cpu_arch_s, os_tag_s, abi_s });
299298 }
300 return std.fmt.allocPrint(allocator, "{s}-{s}", .{ cpu_arch, os_tag });
299 return std.fmt.allocPrint(allocator, "{s}-{s}", .{ cpu_arch_s, os_tag_s });
301300 }
302301
303302 fn hasValue(stack: []const []const u8, needle: []const u8) bool {
......@@ -307,7 +306,7 @@ const TargetMatcher = struct {
307306 return false;
308307 }
309308
310 fn matchesTarget(self: TargetMatcher, targets: []const []const u8) bool {
309 pub fn matchesTarget(self: TargetMatcher, targets: []const []const u8) bool {
311310 for (self.target_strings.items) |t| {
312311 if (hasValue(targets, t)) return true;
313312 }
......@@ -315,26 +314,7 @@ const TargetMatcher = struct {
315314 }
316315
317316 fn matchesArch(self: TargetMatcher, archs: []const []const u8) bool {
318 return hasValue(archs, cpuArchToAppleString(self.target.cpu_arch.?));
319 }
320
321 fn matchesTargetTbd(self: TargetMatcher, tbd: Tbd) !bool {
322 var arena = std.heap.ArenaAllocator.init(self.allocator);
323 defer arena.deinit();
324
325 const targets = switch (tbd) {
326 .v3 => |v3| blk: {
327 var targets = std.ArrayList([]const u8).init(arena.allocator());
328 for (v3.archs) |arch| {
329 const target = try std.fmt.allocPrint(arena.allocator(), "{s}-{s}", .{ arch, v3.platform });
330 try targets.append(target);
331 }
332 break :blk targets.items;
333 },
334 .v4 => |v4| v4.targets,
335 };
336
337 return self.matchesTarget(targets);
317 return hasValue(archs, fmtCpuArch(self.cpu_arch));
338318 }
339319};
340320
......@@ -347,7 +327,7 @@ pub fn parseFromStub(
347327 dependent_libs: anytype,
348328 name: []const u8,
349329) !void {
350 if (lib_stub.inner.len == 0) return error.EmptyStubFile;
330 if (lib_stub.inner.len == 0) return error.NotLibStub;
351331
352332 log.debug("parsing shared library from stub '{s}'", .{name});
353333
......@@ -369,15 +349,16 @@ pub fn parseFromStub(
369349
370350 log.debug(" (install_name '{s}')", .{umbrella_lib.installName()});
371351
372 var matcher = try TargetMatcher.init(allocator, .{
373 .cpu_arch = target.cpu.arch,
374 .os_tag = target.os.tag,
375 .abi = target.abi,
376 });
352 var matcher = try TargetMatcher.init(allocator, target);
377353 defer matcher.deinit();
378354
379355 for (lib_stub.inner, 0..) |elem, stub_index| {
380 if (!(try matcher.matchesTargetTbd(elem))) continue;
356 const targets = try elem.targets(allocator);
357 defer {
358 for (targets) |t| allocator.free(t);
359 allocator.free(targets);
360 }
361 if (!matcher.matchesTarget(targets)) continue;
381362
382363 if (stub_index > 0) {
383364 // TODO I thought that we could switch on presence of `parent-umbrella` map;
......@@ -553,3 +534,23 @@ pub fn parseFromStub(
553534 }
554535 }
555536}
537
538const Dylib = @This();
539
540const std = @import("std");
541const assert = std.debug.assert;
542const fs = std.fs;
543const fmt = std.fmt;
544const log = std.log.scoped(.link);
545const macho = std.macho;
546const math = std.math;
547const mem = std.mem;
548const fat = @import("fat.zig");
549const tapi = @import("../tapi.zig");
550
551const Allocator = mem.Allocator;
552const LibStub = tapi.LibStub;
553const LoadCommandIterator = macho.LoadCommandIterator;
554const MachO = @import("../MachO.zig");
555const Platform = @import("load_commands.zig").Platform;
556const Tbd = tapi.Tbd;
src/link/MachO/Object.zig+137-131
......@@ -2,31 +2,6 @@
22//! Each Object is fully loaded into memory for easier
33//! access into different data within.
44
5const Object = @This();
6
7const std = @import("std");
8const build_options = @import("build_options");
9const assert = std.debug.assert;
10const dwarf = std.dwarf;
11const eh_frame = @import("eh_frame.zig");
12const fs = std.fs;
13const io = std.io;
14const log = std.log.scoped(.link);
15const macho = std.macho;
16const math = std.math;
17const mem = std.mem;
18const sort = std.sort;
19const trace = @import("../../tracy.zig").trace;
20
21const Allocator = mem.Allocator;
22const Atom = @import("ZldAtom.zig");
23const AtomIndex = @import("zld.zig").AtomIndex;
24const DwarfInfo = @import("DwarfInfo.zig");
25const LoadCommandIterator = macho.LoadCommandIterator;
26const Zld = @import("zld.zig").Zld;
27const SymbolWithLoc = @import("zld.zig").SymbolWithLoc;
28const UnwindInfo = @import("UnwindInfo.zig");
29
305name: []const u8,
316mtime: u64,
327contents: []align(@alignOf(u64)) const u8,
......@@ -54,7 +29,7 @@ source_section_index_lookup: []Entry = undefined,
5429/// Can be undefined as set together with in_symtab.
5530strtab_lookup: []u32 = undefined,
5631/// Can be undefined as set together with in_symtab.
57atom_by_index_table: []AtomIndex = undefined,
32atom_by_index_table: []?Atom.Index = undefined,
5833/// Can be undefined as set together with in_symtab.
5934globals_lookup: []i64 = undefined,
6035/// Can be undefined as set together with in_symtab.
......@@ -70,8 +45,8 @@ section_relocs_lookup: std.ArrayListUnmanaged(u32) = .{},
7045/// Data-in-code records sorted by address.
7146data_in_code: std.ArrayListUnmanaged(macho.data_in_code_entry) = .{},
7247
73atoms: std.ArrayListUnmanaged(AtomIndex) = .{},
74exec_atoms: std.ArrayListUnmanaged(AtomIndex) = .{},
48atoms: std.ArrayListUnmanaged(Atom.Index) = .{},
49exec_atoms: std.ArrayListUnmanaged(Atom.Index) = .{},
7550
7651eh_frame_sect_id: ?u8 = null,
7752eh_frame_relocs_lookup: std.AutoArrayHashMapUnmanaged(u32, Record) = .{},
......@@ -91,6 +66,13 @@ const Record = struct {
9166 reloc: Entry,
9267};
9368
69pub fn isObject(file: std.fs.File) bool {
70 const reader = file.reader();
71 const hdr = reader.readStruct(macho.mach_header_64) catch return false;
72 defer file.seekTo(0) catch {};
73 return hdr.filetype == macho.MH_OBJECT;
74}
75
9476pub fn deinit(self: *Object, gpa: Allocator) void {
9577 self.atoms.deinit(gpa);
9678 self.exec_atoms.deinit(gpa);
......@@ -118,36 +100,12 @@ pub fn deinit(self: *Object, gpa: Allocator) void {
118100 self.data_in_code.deinit(gpa);
119101}
120102
121pub fn parse(self: *Object, allocator: Allocator, cpu_arch: std.Target.Cpu.Arch) !void {
103pub fn parse(self: *Object, allocator: Allocator) !void {
122104 var stream = std.io.fixedBufferStream(self.contents);
123105 const reader = stream.reader();
124106
125107 self.header = try reader.readStruct(macho.mach_header_64);
126108
127 if (self.header.filetype != macho.MH_OBJECT) {
128 log.debug("invalid filetype: expected 0x{x}, found 0x{x}", .{
129 macho.MH_OBJECT,
130 self.header.filetype,
131 });
132 return error.NotObject;
133 }
134
135 const this_arch: std.Target.Cpu.Arch = switch (self.header.cputype) {
136 macho.CPU_TYPE_ARM64 => .aarch64,
137 macho.CPU_TYPE_X86_64 => .x86_64,
138 else => |value| {
139 log.err("unsupported cpu architecture 0x{x}", .{value});
140 return error.UnsupportedCpuArchitecture;
141 },
142 };
143 if (this_arch != cpu_arch) {
144 log.err("mismatched cpu architecture: expected {s}, found {s}", .{
145 @tagName(cpu_arch),
146 @tagName(this_arch),
147 });
148 return error.MismatchedCpuArchitecture;
149 }
150
151109 var it = LoadCommandIterator{
152110 .ncmds = self.header.ncmds,
153111 .buffer = self.contents[@sizeOf(macho.mach_header_64)..][0..self.header.sizeofcmds],
......@@ -172,7 +130,7 @@ pub fn parse(self: *Object, allocator: Allocator, cpu_arch: std.Target.Cpu.Arch)
172130 self.reverse_symtab_lookup = try allocator.alloc(u32, self.in_symtab.?.len);
173131 self.strtab_lookup = try allocator.alloc(u32, self.in_symtab.?.len);
174132 self.globals_lookup = try allocator.alloc(i64, self.in_symtab.?.len);
175 self.atom_by_index_table = try allocator.alloc(AtomIndex, self.in_symtab.?.len + nsects);
133 self.atom_by_index_table = try allocator.alloc(?Atom.Index, self.in_symtab.?.len + nsects);
176134 self.relocs_lookup = try allocator.alloc(Entry, self.in_symtab.?.len + nsects);
177135 // This is wasteful but we need to be able to lookup source symbol address after stripping and
178136 // allocating of sections.
......@@ -190,7 +148,7 @@ pub fn parse(self: *Object, allocator: Allocator, cpu_arch: std.Target.Cpu.Arch)
190148 }
191149
192150 @memset(self.globals_lookup, -1);
193 @memset(self.atom_by_index_table, 0);
151 @memset(self.atom_by_index_table, null);
194152 @memset(self.source_section_index_lookup, .{});
195153 @memset(self.relocs_lookup, .{});
196154
......@@ -331,10 +289,10 @@ fn filterSymbolsBySection(symbols: []macho.nlist_64, n_sect: u8) struct {
331289 }
332290 };
333291
334 const index = @import("zld.zig").lsearch(macho.nlist_64, symbols, FirstMatch{
292 const index = MachO.lsearch(macho.nlist_64, symbols, FirstMatch{
335293 .n_sect = n_sect,
336294 });
337 const len = @import("zld.zig").lsearch(macho.nlist_64, symbols[index..], FirstNonMatch{
295 const len = MachO.lsearch(macho.nlist_64, symbols[index..], FirstNonMatch{
338296 .n_sect = n_sect,
339297 });
340298
......@@ -353,10 +311,10 @@ fn filterSymbolsByAddress(symbols: []macho.nlist_64, start_addr: u64, end_addr:
353311 }
354312 };
355313
356 const index = @import("zld.zig").lsearch(macho.nlist_64, symbols, Predicate{
314 const index = MachO.lsearch(macho.nlist_64, symbols, Predicate{
357315 .addr = start_addr,
358316 });
359 const len = @import("zld.zig").lsearch(macho.nlist_64, symbols[index..], Predicate{
317 const len = MachO.lsearch(macho.nlist_64, symbols[index..], Predicate{
360318 .addr = end_addr,
361319 });
362320
......@@ -376,25 +334,32 @@ fn sectionLessThanByAddress(ctx: void, lhs: SortedSection, rhs: SortedSection) b
376334 return lhs.header.addr < rhs.header.addr;
377335}
378336
379pub fn splitIntoAtoms(self: *Object, zld: *Zld, object_id: u32) !void {
337pub const SplitIntoAtomsError = error{
338 OutOfMemory,
339 EndOfStream,
340 MissingEhFrameSection,
341 BadDwarfCfi,
342};
343
344pub fn splitIntoAtoms(self: *Object, macho_file: *MachO, object_id: u32) SplitIntoAtomsError!void {
380345 log.debug("splitting object({d}, {s}) into atoms", .{ object_id, self.name });
381346
382 try self.splitRegularSections(zld, object_id);
383 try self.parseEhFrameSection(zld, object_id);
384 try self.parseUnwindInfo(zld, object_id);
385 try self.parseDataInCode(zld.gpa);
347 try self.splitRegularSections(macho_file, object_id);
348 try self.parseEhFrameSection(macho_file, object_id);
349 try self.parseUnwindInfo(macho_file, object_id);
350 try self.parseDataInCode(macho_file.base.allocator);
386351}
387352
388353/// Splits input regular sections into Atoms.
389354/// If the Object was compiled with `MH_SUBSECTIONS_VIA_SYMBOLS`, splits section
390355/// into subsections where each subsection then represents an Atom.
391pub fn splitRegularSections(self: *Object, zld: *Zld, object_id: u32) !void {
392 const gpa = zld.gpa;
356pub fn splitRegularSections(self: *Object, macho_file: *MachO, object_id: u32) !void {
357 const gpa = macho_file.base.allocator;
393358
394359 const sections = self.getSourceSections();
395360 for (sections, 0..) |sect, id| {
396361 if (sect.isDebug()) continue;
397 const out_sect_id = (try zld.getOutputSection(sect)) orelse {
362 const out_sect_id = (try Atom.getOutputSection(macho_file, sect)) orelse {
398363 log.debug(" unhandled section '{s},{s}'", .{ sect.segName(), sect.sectName() });
399364 continue;
400365 };
......@@ -414,13 +379,13 @@ pub fn splitRegularSections(self: *Object, zld: *Zld, object_id: u32) !void {
414379 if (self.in_symtab == null) {
415380 for (sections, 0..) |sect, id| {
416381 if (sect.isDebug()) continue;
417 const out_sect_id = (try zld.getOutputSection(sect)) orelse continue;
382 const out_sect_id = (try Atom.getOutputSection(macho_file, sect)) orelse continue;
418383 if (sect.size == 0) continue;
419384
420385 const sect_id = @as(u8, @intCast(id));
421386 const sym_index = self.getSectionAliasSymbolIndex(sect_id);
422387 const atom_index = try self.createAtomFromSubsection(
423 zld,
388 macho_file,
424389 object_id,
425390 sym_index,
426391 sym_index,
......@@ -429,7 +394,7 @@ pub fn splitRegularSections(self: *Object, zld: *Zld, object_id: u32) !void {
429394 sect.@"align",
430395 out_sect_id,
431396 );
432 zld.addAtomToSection(atom_index);
397 macho_file.addAtomToSection(atom_index);
433398 }
434399 return;
435400 }
......@@ -437,7 +402,7 @@ pub fn splitRegularSections(self: *Object, zld: *Zld, object_id: u32) !void {
437402 // Well, shit, sometimes compilers skip the dysymtab load command altogether, meaning we
438403 // have to infer the start of undef section in the symtab ourselves.
439404 const iundefsym = blk: {
440 const dysymtab = self.parseDysymtab() orelse {
405 const dysymtab = self.getDysymtab() orelse {
441406 var iundefsym: usize = self.in_symtab.?.len;
442407 while (iundefsym > 0) : (iundefsym -= 1) {
443408 const sym = self.symtab[iundefsym - 1];
......@@ -473,17 +438,17 @@ pub fn splitRegularSections(self: *Object, zld: *Zld, object_id: u32) !void {
473438 log.debug("splitting section '{s},{s}' into atoms", .{ sect.segName(), sect.sectName() });
474439
475440 // Get output segment/section in the final artifact.
476 const out_sect_id = (try zld.getOutputSection(sect)) orelse continue;
441 const out_sect_id = (try Atom.getOutputSection(macho_file, sect)) orelse continue;
477442
478443 log.debug(" output sect({d}, '{s},{s}')", .{
479444 out_sect_id + 1,
480 zld.sections.items(.header)[out_sect_id].segName(),
481 zld.sections.items(.header)[out_sect_id].sectName(),
445 macho_file.sections.items(.header)[out_sect_id].segName(),
446 macho_file.sections.items(.header)[out_sect_id].sectName(),
482447 });
483448
484449 try self.parseRelocs(gpa, section.id);
485450
486 const cpu_arch = zld.options.target.cpu.arch;
451 const cpu_arch = macho_file.base.options.target.cpu.arch;
487452 const sect_loc = filterSymbolsBySection(symtab[sect_sym_index..], sect_id + 1);
488453 const sect_start_index = sect_sym_index + sect_loc.index;
489454
......@@ -499,7 +464,7 @@ pub fn splitRegularSections(self: *Object, zld: *Zld, object_id: u32) !void {
499464 const sym_index = self.getSectionAliasSymbolIndex(sect_id);
500465 const atom_size = first_sym.n_value - sect.addr;
501466 const atom_index = try self.createAtomFromSubsection(
502 zld,
467 macho_file,
503468 object_id,
504469 sym_index,
505470 sym_index,
......@@ -509,9 +474,9 @@ pub fn splitRegularSections(self: *Object, zld: *Zld, object_id: u32) !void {
509474 out_sect_id,
510475 );
511476 if (!sect.isZerofill()) {
512 try self.cacheRelocs(zld, atom_index);
477 try self.cacheRelocs(macho_file, atom_index);
513478 }
514 zld.addAtomToSection(atom_index);
479 macho_file.addAtomToSection(atom_index);
515480 }
516481
517482 var next_sym_index = sect_start_index;
......@@ -535,7 +500,7 @@ pub fn splitRegularSections(self: *Object, zld: *Zld, object_id: u32) !void {
535500 sect.@"align";
536501
537502 const atom_index = try self.createAtomFromSubsection(
538 zld,
503 macho_file,
539504 object_id,
540505 atom_sym_index,
541506 atom_sym_index,
......@@ -554,14 +519,14 @@ pub fn splitRegularSections(self: *Object, zld: *Zld, object_id: u32) !void {
554519 self.atom_by_index_table[alias_index] = atom_index;
555520 }
556521 if (!sect.isZerofill()) {
557 try self.cacheRelocs(zld, atom_index);
522 try self.cacheRelocs(macho_file, atom_index);
558523 }
559 zld.addAtomToSection(atom_index);
524 macho_file.addAtomToSection(atom_index);
560525 }
561526 } else {
562527 const alias_index = self.getSectionAliasSymbolIndex(sect_id);
563528 const atom_index = try self.createAtomFromSubsection(
564 zld,
529 macho_file,
565530 object_id,
566531 alias_index,
567532 sect_start_index,
......@@ -571,16 +536,16 @@ pub fn splitRegularSections(self: *Object, zld: *Zld, object_id: u32) !void {
571536 out_sect_id,
572537 );
573538 if (!sect.isZerofill()) {
574 try self.cacheRelocs(zld, atom_index);
539 try self.cacheRelocs(macho_file, atom_index);
575540 }
576 zld.addAtomToSection(atom_index);
541 macho_file.addAtomToSection(atom_index);
577542 }
578543 }
579544}
580545
581546fn createAtomFromSubsection(
582547 self: *Object,
583 zld: *Zld,
548 macho_file: *MachO,
584549 object_id: u32,
585550 sym_index: u32,
586551 inner_sym_index: u32,
......@@ -588,10 +553,10 @@ fn createAtomFromSubsection(
588553 size: u64,
589554 alignment: u32,
590555 out_sect_id: u8,
591) !AtomIndex {
592 const gpa = zld.gpa;
593 const atom_index = try zld.createEmptyAtom(sym_index, size, alignment);
594 const atom = zld.getAtomPtr(atom_index);
556) !Atom.Index {
557 const gpa = macho_file.base.allocator;
558 const atom_index = try macho_file.createAtom(sym_index, .{ .size = size, .alignment = alignment });
559 const atom = macho_file.getAtomPtr(atom_index);
595560 atom.inner_sym_index = inner_sym_index;
596561 atom.inner_nsyms_trailing = inner_nsyms_trailing;
597562 atom.file = object_id + 1;
......@@ -601,22 +566,22 @@ fn createAtomFromSubsection(
601566 sym_index,
602567 self.getSymbolName(sym_index),
603568 out_sect_id + 1,
604 zld.sections.items(.header)[out_sect_id].segName(),
605 zld.sections.items(.header)[out_sect_id].sectName(),
569 macho_file.sections.items(.header)[out_sect_id].segName(),
570 macho_file.sections.items(.header)[out_sect_id].sectName(),
606571 object_id,
607572 });
608573
609574 try self.atoms.append(gpa, atom_index);
610575 self.atom_by_index_table[sym_index] = atom_index;
611576
612 var it = Atom.getInnerSymbolsIterator(zld, atom_index);
577 var it = Atom.getInnerSymbolsIterator(macho_file, atom_index);
613578 while (it.next()) |sym_loc| {
614 const inner = zld.getSymbolPtr(sym_loc);
579 const inner = macho_file.getSymbolPtr(sym_loc);
615580 inner.n_sect = out_sect_id + 1;
616581 self.atom_by_index_table[sym_loc.sym_index] = atom_index;
617582 }
618583
619 const out_sect = zld.sections.items(.header)[out_sect_id];
584 const out_sect = macho_file.sections.items(.header)[out_sect_id];
620585 if (out_sect.isCode() and
621586 mem.eql(u8, "__TEXT", out_sect.segName()) and
622587 mem.eql(u8, "__text", out_sect.sectName()))
......@@ -648,8 +613,8 @@ fn filterRelocs(
648613 }
649614 };
650615
651 const start = @import("zld.zig").bsearch(macho.relocation_info, relocs, Predicate{ .addr = end_addr });
652 const len = @import("zld.zig").lsearch(macho.relocation_info, relocs[start..], LPredicate{ .addr = start_addr });
616 const start = MachO.bsearch(macho.relocation_info, relocs, Predicate{ .addr = end_addr });
617 const len = MachO.lsearch(macho.relocation_info, relocs[start..], LPredicate{ .addr = start_addr });
653618
654619 return .{ .start = @as(u32, @intCast(start)), .len = @as(u32, @intCast(len)) };
655620}
......@@ -668,8 +633,8 @@ fn parseRelocs(self: *Object, gpa: Allocator, sect_id: u8) !void {
668633 self.section_relocs_lookup.items[sect_id] = start;
669634}
670635
671fn cacheRelocs(self: *Object, zld: *Zld, atom_index: AtomIndex) !void {
672 const atom = zld.getAtom(atom_index);
636fn cacheRelocs(self: *Object, macho_file: *MachO, atom_index: Atom.Index) !void {
637 const atom = macho_file.getAtom(atom_index);
673638
674639 const source_sect_id = if (self.getSourceSymbol(atom.sym_index)) |source_sym| blk: {
675640 break :blk source_sym.n_sect - 1;
......@@ -696,18 +661,19 @@ fn relocGreaterThan(ctx: void, lhs: macho.relocation_info, rhs: macho.relocation
696661 return lhs.r_address > rhs.r_address;
697662}
698663
699fn parseEhFrameSection(self: *Object, zld: *Zld, object_id: u32) !void {
664fn parseEhFrameSection(self: *Object, macho_file: *MachO, object_id: u32) !void {
700665 const sect_id = self.eh_frame_sect_id orelse return;
701666 const sect = self.getSourceSection(sect_id);
702667
703668 log.debug("parsing __TEXT,__eh_frame section", .{});
704669
705 if (zld.getSectionByName("__TEXT", "__eh_frame") == null) {
706 _ = try zld.initSection("__TEXT", "__eh_frame", .{});
670 const gpa = macho_file.base.allocator;
671
672 if (macho_file.eh_frame_section_index == null) {
673 macho_file.eh_frame_section_index = try macho_file.initSection("__TEXT", "__eh_frame", .{});
707674 }
708675
709 const gpa = zld.gpa;
710 const cpu_arch = zld.options.target.cpu.arch;
676 const cpu_arch = macho_file.base.options.target.cpu.arch;
711677 try self.parseRelocs(gpa, sect_id);
712678 const relocs = self.getRelocs(sect_id);
713679
......@@ -745,7 +711,7 @@ fn parseEhFrameSection(self: *Object, zld: *Zld, object_id: u32) !void {
745711 @as(macho.reloc_type_arm64, @enumFromInt(rel.r_type)) == .ARM64_RELOC_UNSIGNED)
746712 break rel;
747713 } else unreachable;
748 const target = Atom.parseRelocTarget(zld, .{
714 const target = Atom.parseRelocTarget(macho_file, .{
749715 .object_id = object_id,
750716 .rel = rel,
751717 .code = it.data[offset..],
......@@ -760,7 +726,7 @@ fn parseEhFrameSection(self: *Object, zld: *Zld, object_id: u32) !void {
760726 });
761727 const target_sym_index = self.getSymbolByAddress(target_address, null);
762728 const target = if (self.getGlobal(target_sym_index)) |global_index|
763 zld.globals.items[global_index]
729 macho_file.globals.items[global_index]
764730 else
765731 SymbolWithLoc{ .sym_index = target_sym_index, .file = object_id + 1 };
766732 break :blk target;
......@@ -786,7 +752,7 @@ fn parseEhFrameSection(self: *Object, zld: *Zld, object_id: u32) !void {
786752 };
787753 log.debug("FDE at offset {x} tracks {s}", .{
788754 offset,
789 zld.getSymbolName(actual_target),
755 macho_file.getSymbolName(actual_target),
790756 });
791757 try self.eh_frame_records_lookup.putNoClobber(gpa, actual_target, offset);
792758 }
......@@ -795,15 +761,21 @@ fn parseEhFrameSection(self: *Object, zld: *Zld, object_id: u32) !void {
795761 }
796762}
797763
798fn parseUnwindInfo(self: *Object, zld: *Zld, object_id: u32) !void {
764fn parseUnwindInfo(self: *Object, macho_file: *MachO, object_id: u32) !void {
765 const gpa = macho_file.base.allocator;
766 const cpu_arch = macho_file.base.options.target.cpu.arch;
799767 const sect_id = self.unwind_info_sect_id orelse {
800768 // If it so happens that the object had `__eh_frame` section defined but no `__compact_unwind`,
801769 // we will try fully synthesising unwind info records to somewhat match Apple ld's
802770 // approach. However, we will only synthesise DWARF records and nothing more. For this reason,
803771 // we still create the output `__TEXT,__unwind_info` section.
804772 if (self.hasEhFrameRecords()) {
805 if (zld.getSectionByName("__TEXT", "__unwind_info") == null) {
806 _ = try zld.initSection("__TEXT", "__unwind_info", .{});
773 if (macho_file.unwind_info_section_index == null) {
774 macho_file.unwind_info_section_index = try macho_file.initSection(
775 "__TEXT",
776 "__unwind_info",
777 .{},
778 );
807779 }
808780 }
809781 return;
......@@ -811,11 +783,8 @@ fn parseUnwindInfo(self: *Object, zld: *Zld, object_id: u32) !void {
811783
812784 log.debug("parsing unwind info in {s}", .{self.name});
813785
814 const gpa = zld.gpa;
815 const cpu_arch = zld.options.target.cpu.arch;
816
817 if (zld.getSectionByName("__TEXT", "__unwind_info") == null) {
818 _ = try zld.initSection("__TEXT", "__unwind_info", .{});
786 if (macho_file.unwind_info_section_index == null) {
787 macho_file.unwind_info_section_index = try macho_file.initSection("__TEXT", "__unwind_info", .{});
819788 }
820789
821790 const unwind_records = self.getUnwindRecords();
......@@ -826,11 +795,7 @@ fn parseUnwindInfo(self: *Object, zld: *Zld, object_id: u32) !void {
826795 if (UnwindInfo.UnwindEncoding.isDwarf(record.compactUnwindEncoding, cpu_arch)) break true;
827796 } else false;
828797
829 if (needs_eh_frame and !self.hasEhFrameRecords()) {
830 log.err("missing __TEXT,__eh_frame section", .{});
831 log.err(" in object {s}", .{self.name});
832 return error.MissingSection;
833 }
798 if (needs_eh_frame and !self.hasEhFrameRecords()) return error.MissingEhFrameSection;
834799
835800 try self.parseRelocs(gpa, sect_id);
836801 const relocs = self.getRelocs(sect_id);
......@@ -850,7 +815,7 @@ fn parseUnwindInfo(self: *Object, zld: *Zld, object_id: u32) !void {
850815
851816 // Find function symbol that this record describes
852817 const rel = relocs[rel_pos.start..][rel_pos.len - 1];
853 const target = Atom.parseRelocTarget(zld, .{
818 const target = Atom.parseRelocTarget(macho_file, .{
854819 .object_id = object_id,
855820 .rel = rel,
856821 .code = mem.asBytes(&record),
......@@ -874,7 +839,7 @@ fn parseUnwindInfo(self: *Object, zld: *Zld, object_id: u32) !void {
874839 };
875840 log.debug("unwind record {d} tracks {s}", .{
876841 record_id,
877 zld.getSymbolName(actual_target),
842 macho_file.getSymbolName(actual_target),
878843 });
879844 try self.unwind_records_lookup.putNoClobber(gpa, actual_target, @intCast(record_id));
880845 }
......@@ -945,16 +910,14 @@ fn diceLessThan(ctx: void, lhs: macho.data_in_code_entry, rhs: macho.data_in_cod
945910 return lhs.offset < rhs.offset;
946911}
947912
948fn parseDysymtab(self: Object) ?macho.dysymtab_command {
913fn getDysymtab(self: Object) ?macho.dysymtab_command {
949914 var it = LoadCommandIterator{
950915 .ncmds = self.header.ncmds,
951916 .buffer = self.contents[@sizeOf(macho.mach_header_64)..][0..self.header.sizeofcmds],
952917 };
953918 while (it.next()) |cmd| {
954919 switch (cmd.cmd()) {
955 .DYSYMTAB => {
956 return cmd.cast(macho.dysymtab_command).?;
957 },
920 .DYSYMTAB => return cmd.cast(macho.dysymtab_command).?,
958921 else => {},
959922 }
960923 } else return null;
......@@ -980,6 +943,26 @@ pub fn parseDwarfInfo(self: Object) DwarfInfo {
980943 return di;
981944}
982945
946/// Returns Platform composed from the first encountered build version type load command:
947/// either LC_BUILD_VERSION or LC_VERSION_MIN_*.
948pub fn getPlatform(self: Object) ?Platform {
949 var it = LoadCommandIterator{
950 .ncmds = self.header.ncmds,
951 .buffer = self.contents[@sizeOf(macho.mach_header_64)..][0..self.header.sizeofcmds],
952 };
953 while (it.next()) |cmd| {
954 switch (cmd.cmd()) {
955 .BUILD_VERSION,
956 .VERSION_MIN_MACOSX,
957 .VERSION_MIN_IPHONEOS,
958 .VERSION_MIN_TVOS,
959 .VERSION_MIN_WATCHOS,
960 => return Platform.fromLoadCommand(cmd),
961 else => {},
962 }
963 } else return null;
964}
965
983966pub fn getSectionContents(self: Object, sect: macho.section_64) []const u8 {
984967 const size = @as(usize, @intCast(sect.size));
985968 return self.contents[sect.offset..][0..size];
......@@ -1050,7 +1033,7 @@ pub fn getSymbolByAddress(self: Object, addr: u64, sect_hint: ?u8) u32 {
10501033 if (sect_hint) |sect_id| {
10511034 if (self.source_section_index_lookup[sect_id].len > 0) {
10521035 const lookup = self.source_section_index_lookup[sect_id];
1053 const target_sym_index = @import("zld.zig").lsearch(
1036 const target_sym_index = MachO.lsearch(
10541037 i64,
10551038 self.source_address_lookup[lookup.start..][0..lookup.len],
10561039 Predicate{ .addr = @as(i64, @intCast(addr)) },
......@@ -1065,7 +1048,7 @@ pub fn getSymbolByAddress(self: Object, addr: u64, sect_hint: ?u8) u32 {
10651048 return self.getSectionAliasSymbolIndex(sect_id);
10661049 }
10671050
1068 const target_sym_index = @import("zld.zig").lsearch(i64, self.source_address_lookup, Predicate{
1051 const target_sym_index = MachO.lsearch(i64, self.source_address_lookup, Predicate{
10691052 .addr = @as(i64, @intCast(addr)),
10701053 });
10711054 assert(target_sym_index > 0);
......@@ -1077,10 +1060,8 @@ pub fn getGlobal(self: Object, sym_index: u32) ?u32 {
10771060 return @as(u32, @intCast(self.globals_lookup[sym_index]));
10781061}
10791062
1080pub fn getAtomIndexForSymbol(self: Object, sym_index: u32) ?AtomIndex {
1081 const atom_index = self.atom_by_index_table[sym_index];
1082 if (atom_index == 0) return null;
1083 return atom_index;
1063pub fn getAtomIndexForSymbol(self: Object, sym_index: u32) ?Atom.Index {
1064 return self.atom_by_index_table[sym_index];
10841065}
10851066
10861067pub fn hasUnwindRecords(self: Object) bool {
......@@ -1109,3 +1090,28 @@ pub fn getEhFrameRecordsIterator(self: Object) eh_frame.Iterator {
11091090pub fn hasDataInCode(self: Object) bool {
11101091 return self.data_in_code.items.len > 0;
11111092}
1093
1094const Object = @This();
1095
1096const std = @import("std");
1097const build_options = @import("build_options");
1098const assert = std.debug.assert;
1099const dwarf = std.dwarf;
1100const eh_frame = @import("eh_frame.zig");
1101const fs = std.fs;
1102const io = std.io;
1103const log = std.log.scoped(.link);
1104const macho = std.macho;
1105const math = std.math;
1106const mem = std.mem;
1107const sort = std.sort;
1108const trace = @import("../../tracy.zig").trace;
1109
1110const Allocator = mem.Allocator;
1111const Atom = @import("Atom.zig");
1112const DwarfInfo = @import("DwarfInfo.zig");
1113const LoadCommandIterator = macho.LoadCommandIterator;
1114const MachO = @import("../MachO.zig");
1115const Platform = @import("load_commands.zig").Platform;
1116const SymbolWithLoc = MachO.SymbolWithLoc;
1117const UnwindInfo = @import("UnwindInfo.zig");
src/link/MachO/Relocation.zig+1-1
......@@ -62,7 +62,7 @@ pub fn getTargetBaseAddress(self: Relocation, macho_file: *MachO) ?u64 {
6262 const index = macho_file.stub_table.lookup.get(self.target) orelse return null;
6363 const header = macho_file.sections.items(.header)[macho_file.stubs_section_index.?];
6464 return header.addr +
65 index * @import("stubs.zig").calcStubEntrySize(macho_file.base.options.target.cpu.arch);
65 index * @import("stubs.zig").stubSize(macho_file.base.options.target.cpu.arch);
6666 }
6767 switch (self.type) {
6868 .got, .got_page, .got_pageoff => {
src/link/MachO/Trie.zig+242-242
......@@ -28,248 +28,6 @@
2828//! After the optional exported symbol information is a byte of how many edges (0-255) that
2929//! this node has leaving it, followed by each edge. Each edge is a zero terminated UTF8 of
3030//! the addition chars in the symbol, followed by a uleb128 offset for the node that edge points to.
31const Trie = @This();
32
33const std = @import("std");
34const mem = std.mem;
35const leb = std.leb;
36const log = std.log.scoped(.link);
37const macho = std.macho;
38const testing = std.testing;
39const assert = std.debug.assert;
40const Allocator = mem.Allocator;
41
42pub const Node = struct {
43 base: *Trie,
44
45 /// Terminal info associated with this node.
46 /// If this node is not a terminal node, info is null.
47 terminal_info: ?struct {
48 /// Export flags associated with this exported symbol.
49 export_flags: u64,
50 /// VM address offset wrt to the section this symbol is defined against.
51 vmaddr_offset: u64,
52 } = null,
53
54 /// Offset of this node in the trie output byte stream.
55 trie_offset: ?u64 = null,
56
57 /// List of all edges originating from this node.
58 edges: std.ArrayListUnmanaged(Edge) = .{},
59
60 node_dirty: bool = true,
61
62 /// Edge connecting to nodes in the trie.
63 pub const Edge = struct {
64 from: *Node,
65 to: *Node,
66 label: []u8,
67
68 fn deinit(self: *Edge, allocator: Allocator) void {
69 self.to.deinit(allocator);
70 allocator.destroy(self.to);
71 allocator.free(self.label);
72 self.from = undefined;
73 self.to = undefined;
74 self.label = undefined;
75 }
76 };
77
78 fn deinit(self: *Node, allocator: Allocator) void {
79 for (self.edges.items) |*edge| {
80 edge.deinit(allocator);
81 }
82 self.edges.deinit(allocator);
83 }
84
85 /// Inserts a new node starting from `self`.
86 fn put(self: *Node, allocator: Allocator, label: []const u8) !*Node {
87 // Check for match with edges from this node.
88 for (self.edges.items) |*edge| {
89 const match = mem.indexOfDiff(u8, edge.label, label) orelse return edge.to;
90 if (match == 0) continue;
91 if (match == edge.label.len) return edge.to.put(allocator, label[match..]);
92
93 // Found a match, need to splice up nodes.
94 // From: A -> B
95 // To: A -> C -> B
96 const mid = try allocator.create(Node);
97 mid.* = .{ .base = self.base };
98 var to_label = try allocator.dupe(u8, edge.label[match..]);
99 allocator.free(edge.label);
100 const to_node = edge.to;
101 edge.to = mid;
102 edge.label = try allocator.dupe(u8, label[0..match]);
103 self.base.node_count += 1;
104
105 try mid.edges.append(allocator, .{
106 .from = mid,
107 .to = to_node,
108 .label = to_label,
109 });
110
111 return if (match == label.len) mid else mid.put(allocator, label[match..]);
112 }
113
114 // Add a new node.
115 const node = try allocator.create(Node);
116 node.* = .{ .base = self.base };
117 self.base.node_count += 1;
118
119 try self.edges.append(allocator, .{
120 .from = self,
121 .to = node,
122 .label = try allocator.dupe(u8, label),
123 });
124
125 return node;
126 }
127
128 /// Recursively parses the node from the input byte stream.
129 fn read(self: *Node, allocator: Allocator, reader: anytype) Trie.ReadError!usize {
130 self.node_dirty = true;
131 const trie_offset = try reader.context.getPos();
132 self.trie_offset = trie_offset;
133
134 var nread: usize = 0;
135
136 const node_size = try leb.readULEB128(u64, reader);
137 if (node_size > 0) {
138 const export_flags = try leb.readULEB128(u64, reader);
139 // TODO Parse special flags.
140 assert(export_flags & macho.EXPORT_SYMBOL_FLAGS_REEXPORT == 0 and
141 export_flags & macho.EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER == 0);
142
143 const vmaddr_offset = try leb.readULEB128(u64, reader);
144
145 self.terminal_info = .{
146 .export_flags = export_flags,
147 .vmaddr_offset = vmaddr_offset,
148 };
149 }
150
151 const nedges = try reader.readByte();
152 self.base.node_count += nedges;
153
154 nread += (try reader.context.getPos()) - trie_offset;
155
156 var i: usize = 0;
157 while (i < nedges) : (i += 1) {
158 const edge_start_pos = try reader.context.getPos();
159
160 const label = blk: {
161 var label_buf = std.ArrayList(u8).init(allocator);
162 while (true) {
163 const next = try reader.readByte();
164 if (next == @as(u8, 0))
165 break;
166 try label_buf.append(next);
167 }
168 break :blk try label_buf.toOwnedSlice();
169 };
170
171 const seek_to = try leb.readULEB128(u64, reader);
172 const return_pos = try reader.context.getPos();
173
174 nread += return_pos - edge_start_pos;
175 try reader.context.seekTo(seek_to);
176
177 const node = try allocator.create(Node);
178 node.* = .{ .base = self.base };
179
180 nread += try node.read(allocator, reader);
181 try self.edges.append(allocator, .{
182 .from = self,
183 .to = node,
184 .label = label,
185 });
186 try reader.context.seekTo(return_pos);
187 }
188
189 return nread;
190 }
191
192 /// Writes this node to a byte stream.
193 /// The children of this node *are* not written to the byte stream
194 /// recursively. To write all nodes to a byte stream in sequence,
195 /// iterate over `Trie.ordered_nodes` and call this method on each node.
196 /// This is one of the requirements of the MachO.
197 /// Panics if `finalize` was not called before calling this method.
198 fn write(self: Node, writer: anytype) !void {
199 assert(!self.node_dirty);
200 if (self.terminal_info) |info| {
201 // Terminal node info: encode export flags and vmaddr offset of this symbol.
202 var info_buf: [@sizeOf(u64) * 2]u8 = undefined;
203 var info_stream = std.io.fixedBufferStream(&info_buf);
204 // TODO Implement for special flags.
205 assert(info.export_flags & macho.EXPORT_SYMBOL_FLAGS_REEXPORT == 0 and
206 info.export_flags & macho.EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER == 0);
207 try leb.writeULEB128(info_stream.writer(), info.export_flags);
208 try leb.writeULEB128(info_stream.writer(), info.vmaddr_offset);
209
210 // Encode the size of the terminal node info.
211 var size_buf: [@sizeOf(u64)]u8 = undefined;
212 var size_stream = std.io.fixedBufferStream(&size_buf);
213 try leb.writeULEB128(size_stream.writer(), info_stream.pos);
214
215 // Now, write them to the output stream.
216 try writer.writeAll(size_buf[0..size_stream.pos]);
217 try writer.writeAll(info_buf[0..info_stream.pos]);
218 } else {
219 // Non-terminal node is delimited by 0 byte.
220 try writer.writeByte(0);
221 }
222 // Write number of edges (max legal number of edges is 256).
223 try writer.writeByte(@as(u8, @intCast(self.edges.items.len)));
224
225 for (self.edges.items) |edge| {
226 // Write edge label and offset to next node in trie.
227 try writer.writeAll(edge.label);
228 try writer.writeByte(0);
229 try leb.writeULEB128(writer, edge.to.trie_offset.?);
230 }
231 }
232
233 const FinalizeResult = struct {
234 /// Current size of this node in bytes.
235 node_size: u64,
236
237 /// True if the trie offset of this node in the output byte stream
238 /// would need updating; false otherwise.
239 updated: bool,
240 };
241
242 /// Updates offset of this node in the output byte stream.
243 fn finalize(self: *Node, offset_in_trie: u64) !FinalizeResult {
244 var stream = std.io.countingWriter(std.io.null_writer);
245 var writer = stream.writer();
246
247 var node_size: u64 = 0;
248 if (self.terminal_info) |info| {
249 try leb.writeULEB128(writer, info.export_flags);
250 try leb.writeULEB128(writer, info.vmaddr_offset);
251 try leb.writeULEB128(writer, stream.bytes_written);
252 } else {
253 node_size += 1; // 0x0 for non-terminal nodes
254 }
255 node_size += 1; // 1 byte for edge count
256
257 for (self.edges.items) |edge| {
258 const next_node_offset = edge.to.trie_offset orelse 0;
259 node_size += edge.label.len + 1;
260 try leb.writeULEB128(writer, next_node_offset);
261 }
262
263 const trie_offset = self.trie_offset orelse 0;
264 const updated = offset_in_trie != trie_offset;
265 self.trie_offset = offset_in_trie;
266 self.node_dirty = false;
267 node_size += stream.bytes_written;
268
269 return FinalizeResult{ .node_size = node_size, .updated = updated };
270 }
271};
272
27331/// The root node of the trie.
27432root: ?*Node = null,
27533
......@@ -611,3 +369,245 @@ test "ordering bug" {
611369 _ = try trie.write(stream.writer());
612370 try expectEqualHexStrings(&exp_buffer, buffer);
613371}
372
373pub const Node = struct {
374 base: *Trie,
375
376 /// Terminal info associated with this node.
377 /// If this node is not a terminal node, info is null.
378 terminal_info: ?struct {
379 /// Export flags associated with this exported symbol.
380 export_flags: u64,
381 /// VM address offset wrt to the section this symbol is defined against.
382 vmaddr_offset: u64,
383 } = null,
384
385 /// Offset of this node in the trie output byte stream.
386 trie_offset: ?u64 = null,
387
388 /// List of all edges originating from this node.
389 edges: std.ArrayListUnmanaged(Edge) = .{},
390
391 node_dirty: bool = true,
392
393 /// Edge connecting to nodes in the trie.
394 pub const Edge = struct {
395 from: *Node,
396 to: *Node,
397 label: []u8,
398
399 fn deinit(self: *Edge, allocator: Allocator) void {
400 self.to.deinit(allocator);
401 allocator.destroy(self.to);
402 allocator.free(self.label);
403 self.from = undefined;
404 self.to = undefined;
405 self.label = undefined;
406 }
407 };
408
409 fn deinit(self: *Node, allocator: Allocator) void {
410 for (self.edges.items) |*edge| {
411 edge.deinit(allocator);
412 }
413 self.edges.deinit(allocator);
414 }
415
416 /// Inserts a new node starting from `self`.
417 fn put(self: *Node, allocator: Allocator, label: []const u8) !*Node {
418 // Check for match with edges from this node.
419 for (self.edges.items) |*edge| {
420 const match = mem.indexOfDiff(u8, edge.label, label) orelse return edge.to;
421 if (match == 0) continue;
422 if (match == edge.label.len) return edge.to.put(allocator, label[match..]);
423
424 // Found a match, need to splice up nodes.
425 // From: A -> B
426 // To: A -> C -> B
427 const mid = try allocator.create(Node);
428 mid.* = .{ .base = self.base };
429 var to_label = try allocator.dupe(u8, edge.label[match..]);
430 allocator.free(edge.label);
431 const to_node = edge.to;
432 edge.to = mid;
433 edge.label = try allocator.dupe(u8, label[0..match]);
434 self.base.node_count += 1;
435
436 try mid.edges.append(allocator, .{
437 .from = mid,
438 .to = to_node,
439 .label = to_label,
440 });
441
442 return if (match == label.len) mid else mid.put(allocator, label[match..]);
443 }
444
445 // Add a new node.
446 const node = try allocator.create(Node);
447 node.* = .{ .base = self.base };
448 self.base.node_count += 1;
449
450 try self.edges.append(allocator, .{
451 .from = self,
452 .to = node,
453 .label = try allocator.dupe(u8, label),
454 });
455
456 return node;
457 }
458
459 /// Recursively parses the node from the input byte stream.
460 fn read(self: *Node, allocator: Allocator, reader: anytype) Trie.ReadError!usize {
461 self.node_dirty = true;
462 const trie_offset = try reader.context.getPos();
463 self.trie_offset = trie_offset;
464
465 var nread: usize = 0;
466
467 const node_size = try leb.readULEB128(u64, reader);
468 if (node_size > 0) {
469 const export_flags = try leb.readULEB128(u64, reader);
470 // TODO Parse special flags.
471 assert(export_flags & macho.EXPORT_SYMBOL_FLAGS_REEXPORT == 0 and
472 export_flags & macho.EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER == 0);
473
474 const vmaddr_offset = try leb.readULEB128(u64, reader);
475
476 self.terminal_info = .{
477 .export_flags = export_flags,
478 .vmaddr_offset = vmaddr_offset,
479 };
480 }
481
482 const nedges = try reader.readByte();
483 self.base.node_count += nedges;
484
485 nread += (try reader.context.getPos()) - trie_offset;
486
487 var i: usize = 0;
488 while (i < nedges) : (i += 1) {
489 const edge_start_pos = try reader.context.getPos();
490
491 const label = blk: {
492 var label_buf = std.ArrayList(u8).init(allocator);
493 while (true) {
494 const next = try reader.readByte();
495 if (next == @as(u8, 0))
496 break;
497 try label_buf.append(next);
498 }
499 break :blk try label_buf.toOwnedSlice();
500 };
501
502 const seek_to = try leb.readULEB128(u64, reader);
503 const return_pos = try reader.context.getPos();
504
505 nread += return_pos - edge_start_pos;
506 try reader.context.seekTo(seek_to);
507
508 const node = try allocator.create(Node);
509 node.* = .{ .base = self.base };
510
511 nread += try node.read(allocator, reader);
512 try self.edges.append(allocator, .{
513 .from = self,
514 .to = node,
515 .label = label,
516 });
517 try reader.context.seekTo(return_pos);
518 }
519
520 return nread;
521 }
522
523 /// Writes this node to a byte stream.
524 /// The children of this node *are* not written to the byte stream
525 /// recursively. To write all nodes to a byte stream in sequence,
526 /// iterate over `Trie.ordered_nodes` and call this method on each node.
527 /// This is one of the requirements of the MachO.
528 /// Panics if `finalize` was not called before calling this method.
529 fn write(self: Node, writer: anytype) !void {
530 assert(!self.node_dirty);
531 if (self.terminal_info) |info| {
532 // Terminal node info: encode export flags and vmaddr offset of this symbol.
533 var info_buf: [@sizeOf(u64) * 2]u8 = undefined;
534 var info_stream = std.io.fixedBufferStream(&info_buf);
535 // TODO Implement for special flags.
536 assert(info.export_flags & macho.EXPORT_SYMBOL_FLAGS_REEXPORT == 0 and
537 info.export_flags & macho.EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER == 0);
538 try leb.writeULEB128(info_stream.writer(), info.export_flags);
539 try leb.writeULEB128(info_stream.writer(), info.vmaddr_offset);
540
541 // Encode the size of the terminal node info.
542 var size_buf: [@sizeOf(u64)]u8 = undefined;
543 var size_stream = std.io.fixedBufferStream(&size_buf);
544 try leb.writeULEB128(size_stream.writer(), info_stream.pos);
545
546 // Now, write them to the output stream.
547 try writer.writeAll(size_buf[0..size_stream.pos]);
548 try writer.writeAll(info_buf[0..info_stream.pos]);
549 } else {
550 // Non-terminal node is delimited by 0 byte.
551 try writer.writeByte(0);
552 }
553 // Write number of edges (max legal number of edges is 256).
554 try writer.writeByte(@as(u8, @intCast(self.edges.items.len)));
555
556 for (self.edges.items) |edge| {
557 // Write edge label and offset to next node in trie.
558 try writer.writeAll(edge.label);
559 try writer.writeByte(0);
560 try leb.writeULEB128(writer, edge.to.trie_offset.?);
561 }
562 }
563
564 const FinalizeResult = struct {
565 /// Current size of this node in bytes.
566 node_size: u64,
567
568 /// True if the trie offset of this node in the output byte stream
569 /// would need updating; false otherwise.
570 updated: bool,
571 };
572
573 /// Updates offset of this node in the output byte stream.
574 fn finalize(self: *Node, offset_in_trie: u64) !FinalizeResult {
575 var stream = std.io.countingWriter(std.io.null_writer);
576 var writer = stream.writer();
577
578 var node_size: u64 = 0;
579 if (self.terminal_info) |info| {
580 try leb.writeULEB128(writer, info.export_flags);
581 try leb.writeULEB128(writer, info.vmaddr_offset);
582 try leb.writeULEB128(writer, stream.bytes_written);
583 } else {
584 node_size += 1; // 0x0 for non-terminal nodes
585 }
586 node_size += 1; // 1 byte for edge count
587
588 for (self.edges.items) |edge| {
589 const next_node_offset = edge.to.trie_offset orelse 0;
590 node_size += edge.label.len + 1;
591 try leb.writeULEB128(writer, next_node_offset);
592 }
593
594 const trie_offset = self.trie_offset orelse 0;
595 const updated = offset_in_trie != trie_offset;
596 self.trie_offset = offset_in_trie;
597 self.node_dirty = false;
598 node_size += stream.bytes_written;
599
600 return FinalizeResult{ .node_size = node_size, .updated = updated };
601 }
602};
603
604const Trie = @This();
605
606const std = @import("std");
607const mem = std.mem;
608const leb = std.leb;
609const log = std.log.scoped(.link);
610const macho = std.macho;
611const testing = std.testing;
612const assert = std.debug.assert;
613const Allocator = mem.Allocator;
src/link/MachO/UnwindInfo.zig+75-80
......@@ -1,26 +1,3 @@
1const UnwindInfo = @This();
2
3const std = @import("std");
4const assert = std.debug.assert;
5const eh_frame = @import("eh_frame.zig");
6const fs = std.fs;
7const leb = std.leb;
8const log = std.log.scoped(.unwind_info);
9const macho = std.macho;
10const math = std.math;
11const mem = std.mem;
12const trace = @import("../../tracy.zig").trace;
13
14const Allocator = mem.Allocator;
15const Atom = @import("ZldAtom.zig");
16const AtomIndex = @import("zld.zig").AtomIndex;
17const EhFrameRecord = eh_frame.EhFrameRecord;
18const Object = @import("Object.zig");
19const SymbolWithLoc = @import("zld.zig").SymbolWithLoc;
20const Zld = @import("zld.zig").Zld;
21
22const N_DEAD = @import("zld.zig").N_DEAD;
23
241gpa: Allocator,
252
263/// List of all unwind records gathered from all objects and sorted
......@@ -204,28 +181,28 @@ pub fn deinit(info: *UnwindInfo) void {
204181 info.lsdas_lookup.deinit(info.gpa);
205182}
206183
207pub fn scanRelocs(zld: *Zld) !void {
208 if (zld.getSectionByName("__TEXT", "__unwind_info") == null) return;
184pub fn scanRelocs(macho_file: *MachO) !void {
185 if (macho_file.unwind_info_section_index == null) return;
209186
210 const cpu_arch = zld.options.target.cpu.arch;
211 for (zld.objects.items, 0..) |*object, object_id| {
187 const cpu_arch = macho_file.base.options.target.cpu.arch;
188 for (macho_file.objects.items, 0..) |*object, object_id| {
212189 const unwind_records = object.getUnwindRecords();
213190 for (object.exec_atoms.items) |atom_index| {
214 var inner_syms_it = Atom.getInnerSymbolsIterator(zld, atom_index);
191 var inner_syms_it = Atom.getInnerSymbolsIterator(macho_file, atom_index);
215192 while (inner_syms_it.next()) |sym| {
216193 const record_id = object.unwind_records_lookup.get(sym) orelse continue;
217194 if (object.unwind_relocs_lookup[record_id].dead) continue;
218195 const record = unwind_records[record_id];
219196 if (!UnwindEncoding.isDwarf(record.compactUnwindEncoding, cpu_arch)) {
220 if (getPersonalityFunctionReloc(zld, @as(u32, @intCast(object_id)), record_id)) |rel| {
197 if (getPersonalityFunctionReloc(macho_file, @as(u32, @intCast(object_id)), record_id)) |rel| {
221198 // Personality function; add GOT pointer.
222 const target = Atom.parseRelocTarget(zld, .{
199 const target = Atom.parseRelocTarget(macho_file, .{
223200 .object_id = @as(u32, @intCast(object_id)),
224201 .rel = rel,
225202 .code = mem.asBytes(&record),
226203 .base_offset = @as(i32, @intCast(record_id * @sizeOf(macho.compact_unwind_entry))),
227204 });
228 try Atom.addGotEntry(zld, target);
205 try macho_file.addGotEntry(target);
229206 }
230207 }
231208 }
......@@ -233,10 +210,10 @@ pub fn scanRelocs(zld: *Zld) !void {
233210 }
234211}
235212
236pub fn collect(info: *UnwindInfo, zld: *Zld) !void {
237 if (zld.getSectionByName("__TEXT", "__unwind_info") == null) return;
213pub fn collect(info: *UnwindInfo, macho_file: *MachO) !void {
214 if (macho_file.unwind_info_section_index == null) return;
238215
239 const cpu_arch = zld.options.target.cpu.arch;
216 const cpu_arch = macho_file.base.options.target.cpu.arch;
240217
241218 var records = std.ArrayList(macho.compact_unwind_entry).init(info.gpa);
242219 defer records.deinit();
......@@ -245,7 +222,7 @@ pub fn collect(info: *UnwindInfo, zld: *Zld) !void {
245222 defer sym_indexes.deinit();
246223
247224 // TODO handle dead stripping
248 for (zld.objects.items, 0..) |*object, object_id| {
225 for (macho_file.objects.items, 0..) |*object, object_id| {
249226 log.debug("collecting unwind records in {s} ({d})", .{ object.name, object_id });
250227 const unwind_records = object.getUnwindRecords();
251228
......@@ -255,7 +232,7 @@ pub fn collect(info: *UnwindInfo, zld: *Zld) !void {
255232 try sym_indexes.ensureUnusedCapacity(object.exec_atoms.items.len);
256233
257234 for (object.exec_atoms.items) |atom_index| {
258 var inner_syms_it = Atom.getInnerSymbolsIterator(zld, atom_index);
235 var inner_syms_it = Atom.getInnerSymbolsIterator(macho_file, atom_index);
259236 var prev_symbol: ?SymbolWithLoc = null;
260237 while (inner_syms_it.next()) |symbol| {
261238 var record = if (object.unwind_records_lookup.get(symbol)) |record_id| blk: {
......@@ -263,14 +240,14 @@ pub fn collect(info: *UnwindInfo, zld: *Zld) !void {
263240 var record = unwind_records[record_id];
264241
265242 if (UnwindEncoding.isDwarf(record.compactUnwindEncoding, cpu_arch)) {
266 try info.collectPersonalityFromDwarf(zld, @as(u32, @intCast(object_id)), symbol, &record);
243 info.collectPersonalityFromDwarf(macho_file, @as(u32, @intCast(object_id)), symbol, &record);
267244 } else {
268245 if (getPersonalityFunctionReloc(
269 zld,
246 macho_file,
270247 @as(u32, @intCast(object_id)),
271248 record_id,
272249 )) |rel| {
273 const target = Atom.parseRelocTarget(zld, .{
250 const target = Atom.parseRelocTarget(macho_file, .{
274251 .object_id = @as(u32, @intCast(object_id)),
275252 .rel = rel,
276253 .code = mem.asBytes(&record),
......@@ -287,8 +264,8 @@ pub fn collect(info: *UnwindInfo, zld: *Zld) !void {
287264 UnwindEncoding.setPersonalityIndex(&record.compactUnwindEncoding, personality_index + 1);
288265 }
289266
290 if (getLsdaReloc(zld, @as(u32, @intCast(object_id)), record_id)) |rel| {
291 const target = Atom.parseRelocTarget(zld, .{
267 if (getLsdaReloc(macho_file, @as(u32, @intCast(object_id)), record_id)) |rel| {
268 const target = Atom.parseRelocTarget(macho_file, .{
292269 .object_id = @as(u32, @intCast(object_id)),
293270 .rel = rel,
294271 .code = mem.asBytes(&record),
......@@ -299,8 +276,8 @@ pub fn collect(info: *UnwindInfo, zld: *Zld) !void {
299276 }
300277 break :blk record;
301278 } else blk: {
302 const sym = zld.getSymbol(symbol);
303 if (sym.n_desc == N_DEAD) continue;
279 const sym = macho_file.getSymbol(symbol);
280 if (sym.n_desc == MachO.N_DEAD) continue;
304281 if (prev_symbol) |prev_sym| {
305282 const prev_addr = object.getSourceSymbol(prev_sym.sym_index).?.n_value;
306283 const curr_addr = object.getSourceSymbol(symbol.sym_index).?.n_value;
......@@ -311,7 +288,7 @@ pub fn collect(info: *UnwindInfo, zld: *Zld) !void {
311288 if (object.eh_frame_records_lookup.get(symbol)) |fde_offset| {
312289 if (object.eh_frame_relocs_lookup.get(fde_offset).?.dead) continue;
313290 var record = nullRecord();
314 try info.collectPersonalityFromDwarf(zld, @as(u32, @intCast(object_id)), symbol, &record);
291 info.collectPersonalityFromDwarf(macho_file, @as(u32, @intCast(object_id)), symbol, &record);
315292 switch (cpu_arch) {
316293 .aarch64 => UnwindEncoding.setMode(&record.compactUnwindEncoding, macho.UNWIND_ARM64_MODE.DWARF),
317294 .x86_64 => UnwindEncoding.setMode(&record.compactUnwindEncoding, macho.UNWIND_X86_64_MODE.DWARF),
......@@ -324,9 +301,9 @@ pub fn collect(info: *UnwindInfo, zld: *Zld) !void {
324301 break :blk nullRecord();
325302 };
326303
327 const atom = zld.getAtom(atom_index);
328 const sym = zld.getSymbol(symbol);
329 assert(sym.n_desc != N_DEAD);
304 const atom = macho_file.getAtom(atom_index);
305 const sym = macho_file.getSymbol(symbol);
306 assert(sym.n_desc != MachO.N_DEAD);
330307 const size = if (inner_syms_it.next()) |next_sym| blk: {
331308 // All this trouble to account for symbol aliases.
332309 // TODO I think that remodelling the linker so that a Symbol references an Atom
......@@ -337,8 +314,8 @@ pub fn collect(info: *UnwindInfo, zld: *Zld) !void {
337314 const curr_addr = object.getSourceSymbol(symbol.sym_index).?.n_value;
338315 const next_addr = object.getSourceSymbol(next_sym.sym_index).?.n_value;
339316 if (next_addr > curr_addr) break :blk next_addr - curr_addr;
340 break :blk zld.getSymbol(atom.getSymbolWithLoc()).n_value + atom.size - sym.n_value;
341 } else zld.getSymbol(atom.getSymbolWithLoc()).n_value + atom.size - sym.n_value;
317 break :blk macho_file.getSymbol(atom.getSymbolWithLoc()).n_value + atom.size - sym.n_value;
318 } else macho_file.getSymbol(atom.getSymbolWithLoc()).n_value + atom.size - sym.n_value;
342319 record.rangeStart = sym.n_value;
343320 record.rangeLength = @as(u32, @intCast(size));
344321
......@@ -519,23 +496,23 @@ pub fn collect(info: *UnwindInfo, zld: *Zld) !void {
519496
520497fn collectPersonalityFromDwarf(
521498 info: *UnwindInfo,
522 zld: *Zld,
499 macho_file: *MachO,
523500 object_id: u32,
524501 sym_loc: SymbolWithLoc,
525502 record: *macho.compact_unwind_entry,
526) !void {
527 const object = &zld.objects.items[object_id];
503) void {
504 const object = &macho_file.objects.items[object_id];
528505 var it = object.getEhFrameRecordsIterator();
529506 const fde_offset = object.eh_frame_records_lookup.get(sym_loc).?;
530507 it.seekTo(fde_offset);
531 const fde = (try it.next()).?;
532 const cie_ptr = fde.getCiePointerSource(object_id, zld, fde_offset);
508 const fde = (it.next() catch return).?; // We don't care about the error since we already handled it
509 const cie_ptr = fde.getCiePointerSource(object_id, macho_file, fde_offset);
533510 const cie_offset = fde_offset + 4 - cie_ptr;
534511 it.seekTo(cie_offset);
535 const cie = (try it.next()).?;
512 const cie = (it.next() catch return).?; // We don't care about the error since we already handled it
536513
537514 if (cie.getPersonalityPointerReloc(
538 zld,
515 macho_file,
539516 @as(u32, @intCast(object_id)),
540517 cie_offset,
541518 )) |target| {
......@@ -551,9 +528,9 @@ fn collectPersonalityFromDwarf(
551528 }
552529}
553530
554pub fn calcSectionSize(info: UnwindInfo, zld: *Zld) !void {
555 const sect_id = zld.getSectionByName("__TEXT", "__unwind_info") orelse return;
556 const sect = &zld.sections.items(.header)[sect_id];
531pub fn calcSectionSize(info: UnwindInfo, macho_file: *MachO) void {
532 const sect_id = macho_file.unwind_info_section_index orelse return;
533 const sect = &macho_file.sections.items(.header)[sect_id];
557534 sect.@"align" = 2;
558535 sect.size = info.calcRequiredSize();
559536}
......@@ -570,25 +547,23 @@ fn calcRequiredSize(info: UnwindInfo) usize {
570547 return total_size;
571548}
572549
573pub fn write(info: *UnwindInfo, zld: *Zld) !void {
574 const sect_id = zld.getSectionByName("__TEXT", "__unwind_info") orelse return;
575 const sect = &zld.sections.items(.header)[sect_id];
576 const seg_id = zld.sections.items(.segment_index)[sect_id];
577 const seg = zld.segments.items[seg_id];
550pub fn write(info: *UnwindInfo, macho_file: *MachO) !void {
551 const sect_id = macho_file.unwind_info_section_index orelse return;
552 const sect = &macho_file.sections.items(.header)[sect_id];
553 const seg_id = macho_file.sections.items(.segment_index)[sect_id];
554 const seg = macho_file.segments.items[seg_id];
578555
579 const text_sect_id = zld.getSectionByName("__TEXT", "__text").?;
580 const text_sect = zld.sections.items(.header)[text_sect_id];
556 const text_sect_id = macho_file.text_section_index.?;
557 const text_sect = macho_file.sections.items(.header)[text_sect_id];
581558
582559 var personalities: [max_personalities]u32 = undefined;
583 const cpu_arch = zld.options.target.cpu.arch;
560 const cpu_arch = macho_file.base.options.target.cpu.arch;
584561
585562 log.debug("Personalities:", .{});
586563 for (info.personalities[0..info.personalities_count], 0..) |target, i| {
587 const atom_index = zld.getGotAtomIndexForSymbol(target).?;
588 const atom = zld.getAtom(atom_index);
589 const sym = zld.getSymbol(atom.getSymbolWithLoc());
590 personalities[i] = @as(u32, @intCast(sym.n_value - seg.vmaddr));
591 log.debug(" {d}: 0x{x} ({s})", .{ i, personalities[i], zld.getSymbolName(target) });
564 const addr = macho_file.getGotEntryAddress(target).?;
565 personalities[i] = @as(u32, @intCast(addr - seg.vmaddr));
566 log.debug(" {d}: 0x{x} ({s})", .{ i, personalities[i], macho_file.getSymbolName(target) });
592567 }
593568
594569 for (info.records.items) |*rec| {
......@@ -602,7 +577,7 @@ pub fn write(info: *UnwindInfo, zld: *Zld) !void {
602577 if (rec.compactUnwindEncoding > 0 and !UnwindEncoding.isDwarf(rec.compactUnwindEncoding, cpu_arch)) {
603578 const lsda_target = @as(SymbolWithLoc, @bitCast(rec.lsda));
604579 if (lsda_target.getFile()) |_| {
605 const sym = zld.getSymbol(lsda_target);
580 const sym = macho_file.getSymbol(lsda_target);
606581 rec.lsda = sym.n_value - seg.vmaddr;
607582 }
608583 }
......@@ -692,11 +667,11 @@ pub fn write(info: *UnwindInfo, zld: *Zld) !void {
692667 @memset(buffer.items[offset..], 0);
693668 }
694669
695 try zld.file.pwriteAll(buffer.items, sect.offset);
670 try macho_file.base.file.?.pwriteAll(buffer.items, sect.offset);
696671}
697672
698fn getRelocs(zld: *Zld, object_id: u32, record_id: usize) []const macho.relocation_info {
699 const object = &zld.objects.items[object_id];
673fn getRelocs(macho_file: *MachO, object_id: u32, record_id: usize) []const macho.relocation_info {
674 const object = &macho_file.objects.items[object_id];
700675 assert(object.hasUnwindRecords());
701676 const rel_pos = object.unwind_relocs_lookup[record_id].reloc;
702677 const relocs = object.getRelocs(object.unwind_info_sect_id.?);
......@@ -710,11 +685,11 @@ fn isPersonalityFunction(record_id: usize, rel: macho.relocation_info) bool {
710685}
711686
712687pub fn getPersonalityFunctionReloc(
713 zld: *Zld,
688 macho_file: *MachO,
714689 object_id: u32,
715690 record_id: usize,
716691) ?macho.relocation_info {
717 const relocs = getRelocs(zld, object_id, record_id);
692 const relocs = getRelocs(macho_file, object_id, record_id);
718693 for (relocs) |rel| {
719694 if (isPersonalityFunction(record_id, rel)) return rel;
720695 }
......@@ -738,8 +713,8 @@ fn isLsda(record_id: usize, rel: macho.relocation_info) bool {
738713 return rel_offset == 24;
739714}
740715
741pub fn getLsdaReloc(zld: *Zld, object_id: u32, record_id: usize) ?macho.relocation_info {
742 const relocs = getRelocs(zld, object_id, record_id);
716pub fn getLsdaReloc(macho_file: *MachO, object_id: u32, record_id: usize) ?macho.relocation_info {
717 const relocs = getRelocs(macho_file, object_id, record_id);
743718 for (relocs) |rel| {
744719 if (isLsda(record_id, rel)) return rel;
745720 }
......@@ -831,3 +806,23 @@ pub const UnwindEncoding = struct {
831806 enc.* |= offset;
832807 }
833808};
809
810const UnwindInfo = @This();
811
812const std = @import("std");
813const assert = std.debug.assert;
814const eh_frame = @import("eh_frame.zig");
815const fs = std.fs;
816const leb = std.leb;
817const log = std.log.scoped(.unwind_info);
818const macho = std.macho;
819const math = std.math;
820const mem = std.mem;
821const trace = @import("../../tracy.zig").trace;
822
823const Allocator = mem.Allocator;
824const Atom = @import("Atom.zig");
825const EhFrameRecord = eh_frame.EhFrameRecord;
826const MachO = @import("../MachO.zig");
827const Object = @import("Object.zig");
828const SymbolWithLoc = MachO.SymbolWithLoc;
src/link/MachO/ZldAtom.zig deleted-1012
......@@ -1,1012 +0,0 @@
1//! An atom is a single smallest unit of measure that will get an
2//! allocated virtual memory address in the final linked image.
3//! For example, we parse each input section within an input relocatable
4//! object file into a set of atoms which are then laid out contiguously
5//! as they were defined in the input file.
6
7const Atom = @This();
8
9const std = @import("std");
10const build_options = @import("build_options");
11const aarch64 = @import("../../arch/aarch64/bits.zig");
12const assert = std.debug.assert;
13const log = std.log.scoped(.atom);
14const macho = std.macho;
15const math = std.math;
16const mem = std.mem;
17const meta = std.meta;
18const trace = @import("../../tracy.zig").trace;
19
20const Allocator = mem.Allocator;
21const Arch = std.Target.Cpu.Arch;
22const AtomIndex = @import("zld.zig").AtomIndex;
23const Object = @import("Object.zig");
24const Relocation = @import("Relocation.zig");
25const SymbolWithLoc = @import("zld.zig").SymbolWithLoc;
26const Zld = @import("zld.zig").Zld;
27
28/// Each Atom always gets a symbol with the fully qualified name.
29/// The symbol can reside in any object file context structure in `symtab` array
30/// (see `Object`), or if the symbol is a synthetic symbol such as a GOT cell or
31/// a stub trampoline, it can be found in the linkers `locals` arraylist.
32sym_index: u32,
33
34/// 0 means an Atom is a synthetic Atom such as a GOT cell defined by the linker.
35/// Otherwise, it is the index into appropriate object file (indexing from 1).
36/// Prefer using `getFile()` helper to get the file index out rather than using
37/// the field directly.
38file: u32,
39
40/// If this Atom is not a synthetic Atom, i.e., references a subsection in an
41/// Object file, `inner_sym_index` and `inner_nsyms_trailing` tell where and if
42/// this Atom contains any additional symbol references that fall within this Atom's
43/// address range. These could for example be an alias symbol which can be used
44/// internally by the relocation records, or if the Object file couldn't be split
45/// into subsections, this Atom may encompass an entire input section.
46inner_sym_index: u32,
47inner_nsyms_trailing: u32,
48
49/// Size of this atom.
50size: u64,
51
52/// Alignment of this atom as a power of 2.
53/// For instance, aligmment of 0 should be read as 2^0 = 1 byte aligned.
54alignment: u32,
55
56/// Points to the previous and next neighbours
57next_index: ?AtomIndex,
58prev_index: ?AtomIndex,
59
60pub const empty = Atom{
61 .sym_index = 0,
62 .inner_sym_index = 0,
63 .inner_nsyms_trailing = 0,
64 .file = 0,
65 .size = 0,
66 .alignment = 0,
67 .prev_index = null,
68 .next_index = null,
69};
70
71/// Returns `null` if the Atom is a synthetic Atom.
72/// Otherwise, returns an index into an array of Objects.
73pub fn getFile(self: Atom) ?u32 {
74 if (self.file == 0) return null;
75 return self.file - 1;
76}
77
78pub inline fn getSymbolWithLoc(self: Atom) SymbolWithLoc {
79 return .{
80 .sym_index = self.sym_index,
81 .file = self.file,
82 };
83}
84
85const InnerSymIterator = struct {
86 sym_index: u32,
87 nsyms: u32,
88 file: u32,
89 pos: u32 = 0,
90
91 pub fn next(it: *@This()) ?SymbolWithLoc {
92 if (it.pos == it.nsyms) return null;
93 const res = SymbolWithLoc{ .sym_index = it.sym_index + it.pos, .file = it.file };
94 it.pos += 1;
95 return res;
96 }
97};
98
99/// Returns an iterator over potentially contained symbols.
100/// Panics when called on a synthetic Atom.
101pub fn getInnerSymbolsIterator(zld: *Zld, atom_index: AtomIndex) InnerSymIterator {
102 const atom = zld.getAtom(atom_index);
103 assert(atom.getFile() != null);
104 return .{
105 .sym_index = atom.inner_sym_index,
106 .nsyms = atom.inner_nsyms_trailing,
107 .file = atom.file,
108 };
109}
110
111/// Returns a section alias symbol if one is defined.
112/// An alias symbol is used to represent the start of an input section
113/// if there were no symbols defined within that range.
114/// Alias symbols are only used on x86_64.
115pub fn getSectionAlias(zld: *Zld, atom_index: AtomIndex) ?SymbolWithLoc {
116 const atom = zld.getAtom(atom_index);
117 assert(atom.getFile() != null);
118
119 const object = zld.objects.items[atom.getFile().?];
120 const nbase = @as(u32, @intCast(object.in_symtab.?.len));
121 const ntotal = @as(u32, @intCast(object.symtab.len));
122 var sym_index: u32 = nbase;
123 while (sym_index < ntotal) : (sym_index += 1) {
124 if (object.getAtomIndexForSymbol(sym_index)) |other_atom_index| {
125 if (other_atom_index == atom_index) return SymbolWithLoc{
126 .sym_index = sym_index,
127 .file = atom.file,
128 };
129 }
130 }
131 return null;
132}
133
134/// Given an index into a contained symbol within, calculates an offset wrt
135/// the start of this Atom.
136pub fn calcInnerSymbolOffset(zld: *Zld, atom_index: AtomIndex, sym_index: u32) u64 {
137 const atom = zld.getAtom(atom_index);
138 assert(atom.getFile() != null);
139
140 if (atom.sym_index == sym_index) return 0;
141
142 const object = zld.objects.items[atom.getFile().?];
143 const source_sym = object.getSourceSymbol(sym_index).?;
144 const base_addr = if (object.getSourceSymbol(atom.sym_index)) |sym|
145 sym.n_value
146 else blk: {
147 const nbase = @as(u32, @intCast(object.in_symtab.?.len));
148 const sect_id = @as(u8, @intCast(atom.sym_index - nbase));
149 const source_sect = object.getSourceSection(sect_id);
150 break :blk source_sect.addr;
151 };
152 return source_sym.n_value - base_addr;
153}
154
155pub fn scanAtomRelocs(zld: *Zld, atom_index: AtomIndex, relocs: []align(1) const macho.relocation_info) !void {
156 const arch = zld.options.target.cpu.arch;
157 const atom = zld.getAtom(atom_index);
158 assert(atom.getFile() != null); // synthetic atoms do not have relocs
159
160 return switch (arch) {
161 .aarch64 => scanAtomRelocsArm64(zld, atom_index, relocs),
162 .x86_64 => scanAtomRelocsX86(zld, atom_index, relocs),
163 else => unreachable,
164 };
165}
166
167const RelocContext = struct {
168 base_addr: i64 = 0,
169 base_offset: i32 = 0,
170};
171
172pub fn getRelocContext(zld: *Zld, atom_index: AtomIndex) RelocContext {
173 const atom = zld.getAtom(atom_index);
174 assert(atom.getFile() != null); // synthetic atoms do not have relocs
175
176 const object = zld.objects.items[atom.getFile().?];
177 if (object.getSourceSymbol(atom.sym_index)) |source_sym| {
178 const source_sect = object.getSourceSection(source_sym.n_sect - 1);
179 return .{
180 .base_addr = @as(i64, @intCast(source_sect.addr)),
181 .base_offset = @as(i32, @intCast(source_sym.n_value - source_sect.addr)),
182 };
183 }
184 const nbase = @as(u32, @intCast(object.in_symtab.?.len));
185 const sect_id = @as(u8, @intCast(atom.sym_index - nbase));
186 const source_sect = object.getSourceSection(sect_id);
187 return .{
188 .base_addr = @as(i64, @intCast(source_sect.addr)),
189 .base_offset = 0,
190 };
191}
192
193pub fn parseRelocTarget(zld: *Zld, ctx: struct {
194 object_id: u32,
195 rel: macho.relocation_info,
196 code: []const u8,
197 base_addr: i64 = 0,
198 base_offset: i32 = 0,
199}) SymbolWithLoc {
200 const tracy = trace(@src());
201 defer tracy.end();
202
203 const object = &zld.objects.items[ctx.object_id];
204 log.debug("parsing reloc target in object({d}) '{s}' ", .{ ctx.object_id, object.name });
205
206 const sym_index = if (ctx.rel.r_extern == 0) sym_index: {
207 const sect_id = @as(u8, @intCast(ctx.rel.r_symbolnum - 1));
208 const rel_offset = @as(u32, @intCast(ctx.rel.r_address - ctx.base_offset));
209
210 const address_in_section = if (ctx.rel.r_pcrel == 0) blk: {
211 break :blk if (ctx.rel.r_length == 3)
212 mem.readIntLittle(u64, ctx.code[rel_offset..][0..8])
213 else
214 mem.readIntLittle(u32, ctx.code[rel_offset..][0..4]);
215 } else blk: {
216 assert(zld.options.target.cpu.arch == .x86_64);
217 const correction: u3 = switch (@as(macho.reloc_type_x86_64, @enumFromInt(ctx.rel.r_type))) {
218 .X86_64_RELOC_SIGNED => 0,
219 .X86_64_RELOC_SIGNED_1 => 1,
220 .X86_64_RELOC_SIGNED_2 => 2,
221 .X86_64_RELOC_SIGNED_4 => 4,
222 else => unreachable,
223 };
224 const addend = mem.readIntLittle(i32, ctx.code[rel_offset..][0..4]);
225 const target_address = @as(i64, @intCast(ctx.base_addr)) + ctx.rel.r_address + 4 + correction + addend;
226 break :blk @as(u64, @intCast(target_address));
227 };
228
229 // Find containing atom
230 log.debug(" | locating symbol by address @{x} in section {d}", .{ address_in_section, sect_id });
231 break :sym_index object.getSymbolByAddress(address_in_section, sect_id);
232 } else object.reverse_symtab_lookup[ctx.rel.r_symbolnum];
233
234 const sym_loc = SymbolWithLoc{ .sym_index = sym_index, .file = ctx.object_id + 1 };
235 const sym = zld.getSymbol(sym_loc);
236 const target = if (sym.sect() and !sym.ext())
237 sym_loc
238 else if (object.getGlobal(sym_index)) |global_index|
239 zld.globals.items[global_index]
240 else
241 sym_loc;
242 log.debug(" | target %{d} ('{s}') in object({?d})", .{
243 target.sym_index,
244 zld.getSymbolName(target),
245 target.getFile(),
246 });
247 return target;
248}
249
250pub fn getRelocTargetAtomIndex(zld: *Zld, target: SymbolWithLoc, is_via_got: bool) ?AtomIndex {
251 if (is_via_got) {
252 return zld.getGotAtomIndexForSymbol(target).?; // panic means fatal error
253 }
254 if (zld.getStubsAtomIndexForSymbol(target)) |stubs_atom| return stubs_atom;
255 if (zld.getTlvPtrAtomIndexForSymbol(target)) |tlv_ptr_atom| return tlv_ptr_atom;
256
257 if (target.getFile() == null) {
258 const target_sym_name = zld.getSymbolName(target);
259 if (mem.eql(u8, "__mh_execute_header", target_sym_name)) return null;
260 if (mem.eql(u8, "___dso_handle", target_sym_name)) return null;
261
262 unreachable; // referenced symbol not found
263 }
264
265 const object = zld.objects.items[target.getFile().?];
266 return object.getAtomIndexForSymbol(target.sym_index);
267}
268
269fn scanAtomRelocsArm64(zld: *Zld, atom_index: AtomIndex, relocs: []align(1) const macho.relocation_info) !void {
270 for (relocs) |rel| {
271 const rel_type = @as(macho.reloc_type_arm64, @enumFromInt(rel.r_type));
272
273 switch (rel_type) {
274 .ARM64_RELOC_ADDEND, .ARM64_RELOC_SUBTRACTOR => continue,
275 else => {},
276 }
277
278 if (rel.r_extern == 0) continue;
279
280 const atom = zld.getAtom(atom_index);
281 const object = &zld.objects.items[atom.getFile().?];
282 const sym_index = object.reverse_symtab_lookup[rel.r_symbolnum];
283 const sym_loc = SymbolWithLoc{
284 .sym_index = sym_index,
285 .file = atom.file,
286 };
287
288 const target = if (object.getGlobal(sym_index)) |global_index|
289 zld.globals.items[global_index]
290 else
291 sym_loc;
292
293 switch (rel_type) {
294 .ARM64_RELOC_BRANCH26 => {
295 // TODO rewrite relocation
296 try addStub(zld, target);
297 },
298 .ARM64_RELOC_GOT_LOAD_PAGE21,
299 .ARM64_RELOC_GOT_LOAD_PAGEOFF12,
300 .ARM64_RELOC_POINTER_TO_GOT,
301 => {
302 // TODO rewrite relocation
303 try addGotEntry(zld, target);
304 },
305 .ARM64_RELOC_TLVP_LOAD_PAGE21,
306 .ARM64_RELOC_TLVP_LOAD_PAGEOFF12,
307 => {
308 try addTlvPtrEntry(zld, target);
309 },
310 else => {},
311 }
312 }
313}
314
315fn scanAtomRelocsX86(zld: *Zld, atom_index: AtomIndex, relocs: []align(1) const macho.relocation_info) !void {
316 for (relocs) |rel| {
317 const rel_type = @as(macho.reloc_type_x86_64, @enumFromInt(rel.r_type));
318
319 switch (rel_type) {
320 .X86_64_RELOC_SUBTRACTOR => continue,
321 else => {},
322 }
323
324 if (rel.r_extern == 0) continue;
325
326 const atom = zld.getAtom(atom_index);
327 const object = &zld.objects.items[atom.getFile().?];
328 const sym_index = object.reverse_symtab_lookup[rel.r_symbolnum];
329 const sym_loc = SymbolWithLoc{
330 .sym_index = sym_index,
331 .file = atom.file,
332 };
333
334 const target = if (object.getGlobal(sym_index)) |global_index|
335 zld.globals.items[global_index]
336 else
337 sym_loc;
338
339 switch (rel_type) {
340 .X86_64_RELOC_BRANCH => {
341 // TODO rewrite relocation
342 try addStub(zld, target);
343 },
344 .X86_64_RELOC_GOT, .X86_64_RELOC_GOT_LOAD => {
345 // TODO rewrite relocation
346 try addGotEntry(zld, target);
347 },
348 .X86_64_RELOC_TLV => {
349 try addTlvPtrEntry(zld, target);
350 },
351 else => {},
352 }
353 }
354}
355
356fn addTlvPtrEntry(zld: *Zld, target: SymbolWithLoc) !void {
357 const target_sym = zld.getSymbol(target);
358 if (!target_sym.undf()) return;
359 if (zld.tlv_ptr_table.contains(target)) return;
360
361 const gpa = zld.gpa;
362 const atom_index = try zld.createTlvPtrAtom();
363 const tlv_ptr_index = @as(u32, @intCast(zld.tlv_ptr_entries.items.len));
364 try zld.tlv_ptr_entries.append(gpa, .{
365 .target = target,
366 .atom_index = atom_index,
367 });
368 try zld.tlv_ptr_table.putNoClobber(gpa, target, tlv_ptr_index);
369}
370
371pub fn addGotEntry(zld: *Zld, target: SymbolWithLoc) !void {
372 if (zld.got_table.contains(target)) return;
373 const gpa = zld.gpa;
374 const atom_index = try zld.createGotAtom();
375 const got_index = @as(u32, @intCast(zld.got_entries.items.len));
376 try zld.got_entries.append(gpa, .{
377 .target = target,
378 .atom_index = atom_index,
379 });
380 try zld.got_table.putNoClobber(gpa, target, got_index);
381}
382
383pub fn addStub(zld: *Zld, target: SymbolWithLoc) !void {
384 const target_sym = zld.getSymbol(target);
385 if (!target_sym.undf()) return;
386 if (zld.stubs_table.contains(target)) return;
387
388 const gpa = zld.gpa;
389 _ = try zld.createStubHelperAtom();
390 _ = try zld.createLazyPointerAtom();
391 const atom_index = try zld.createStubAtom();
392 const stubs_index = @as(u32, @intCast(zld.stubs.items.len));
393 try zld.stubs.append(gpa, .{
394 .target = target,
395 .atom_index = atom_index,
396 });
397 try zld.stubs_table.putNoClobber(gpa, target, stubs_index);
398}
399
400pub fn resolveRelocs(
401 zld: *Zld,
402 atom_index: AtomIndex,
403 atom_code: []u8,
404 atom_relocs: []align(1) const macho.relocation_info,
405) !void {
406 const arch = zld.options.target.cpu.arch;
407 const atom = zld.getAtom(atom_index);
408 assert(atom.getFile() != null); // synthetic atoms do not have relocs
409
410 log.debug("resolving relocations in ATOM(%{d}, '{s}')", .{
411 atom.sym_index,
412 zld.getSymbolName(atom.getSymbolWithLoc()),
413 });
414
415 const ctx = getRelocContext(zld, atom_index);
416
417 return switch (arch) {
418 .aarch64 => resolveRelocsArm64(zld, atom_index, atom_code, atom_relocs, ctx),
419 .x86_64 => resolveRelocsX86(zld, atom_index, atom_code, atom_relocs, ctx),
420 else => unreachable,
421 };
422}
423
424pub fn getRelocTargetAddress(zld: *Zld, target: SymbolWithLoc, is_via_got: bool, is_tlv: bool) !u64 {
425 const target_atom_index = getRelocTargetAtomIndex(zld, target, is_via_got) orelse {
426 // If there is no atom for target, we still need to check for special, atom-less
427 // symbols such as `___dso_handle`.
428 const target_name = zld.getSymbolName(target);
429 const atomless_sym = zld.getSymbol(target);
430 log.debug(" | atomless target '{s}'", .{target_name});
431 return atomless_sym.n_value;
432 };
433 const target_atom = zld.getAtom(target_atom_index);
434 log.debug(" | target ATOM(%{d}, '{s}') in object({?})", .{
435 target_atom.sym_index,
436 zld.getSymbolName(target_atom.getSymbolWithLoc()),
437 target_atom.getFile(),
438 });
439
440 const target_sym = zld.getSymbol(target_atom.getSymbolWithLoc());
441 assert(target_sym.n_desc != @import("zld.zig").N_DEAD);
442
443 // If `target` is contained within the target atom, pull its address value.
444 const offset = if (target_atom.getFile() != null) blk: {
445 const object = zld.objects.items[target_atom.getFile().?];
446 break :blk if (object.getSourceSymbol(target.sym_index)) |_|
447 Atom.calcInnerSymbolOffset(zld, target_atom_index, target.sym_index)
448 else
449 0; // section alias
450 } else 0;
451 const base_address: u64 = if (is_tlv) base_address: {
452 // For TLV relocations, the value specified as a relocation is the displacement from the
453 // TLV initializer (either value in __thread_data or zero-init in __thread_bss) to the first
454 // defined TLV template init section in the following order:
455 // * wrt to __thread_data if defined, then
456 // * wrt to __thread_bss
457 const sect_id: u16 = sect_id: {
458 if (zld.getSectionByName("__DATA", "__thread_data")) |i| {
459 break :sect_id i;
460 } else if (zld.getSectionByName("__DATA", "__thread_bss")) |i| {
461 break :sect_id i;
462 } else {
463 log.err("threadlocal variables present but no initializer sections found", .{});
464 log.err(" __thread_data not found", .{});
465 log.err(" __thread_bss not found", .{});
466 return error.FailedToResolveRelocationTarget;
467 }
468 };
469 break :base_address zld.sections.items(.header)[sect_id].addr;
470 } else 0;
471 return target_sym.n_value + offset - base_address;
472}
473
474fn resolveRelocsArm64(
475 zld: *Zld,
476 atom_index: AtomIndex,
477 atom_code: []u8,
478 atom_relocs: []align(1) const macho.relocation_info,
479 context: RelocContext,
480) !void {
481 const atom = zld.getAtom(atom_index);
482 const object = zld.objects.items[atom.getFile().?];
483
484 var addend: ?i64 = null;
485 var subtractor: ?SymbolWithLoc = null;
486
487 for (atom_relocs) |rel| {
488 const rel_type = @as(macho.reloc_type_arm64, @enumFromInt(rel.r_type));
489
490 switch (rel_type) {
491 .ARM64_RELOC_ADDEND => {
492 assert(addend == null);
493
494 log.debug(" RELA({s}) @ {x} => {x}", .{ @tagName(rel_type), rel.r_address, rel.r_symbolnum });
495
496 addend = rel.r_symbolnum;
497 continue;
498 },
499 .ARM64_RELOC_SUBTRACTOR => {
500 assert(subtractor == null);
501
502 log.debug(" RELA({s}) @ {x} => %{d} in object({?d})", .{
503 @tagName(rel_type),
504 rel.r_address,
505 rel.r_symbolnum,
506 atom.getFile(),
507 });
508
509 subtractor = parseRelocTarget(zld, .{
510 .object_id = atom.getFile().?,
511 .rel = rel,
512 .code = atom_code,
513 .base_addr = context.base_addr,
514 .base_offset = context.base_offset,
515 });
516 continue;
517 },
518 else => {},
519 }
520
521 const target = parseRelocTarget(zld, .{
522 .object_id = atom.getFile().?,
523 .rel = rel,
524 .code = atom_code,
525 .base_addr = context.base_addr,
526 .base_offset = context.base_offset,
527 });
528 const rel_offset = @as(u32, @intCast(rel.r_address - context.base_offset));
529
530 log.debug(" RELA({s}) @ {x} => %{d} ('{s}') in object({?})", .{
531 @tagName(rel_type),
532 rel.r_address,
533 target.sym_index,
534 zld.getSymbolName(target),
535 target.getFile(),
536 });
537
538 const source_addr = blk: {
539 const source_sym = zld.getSymbol(atom.getSymbolWithLoc());
540 break :blk source_sym.n_value + rel_offset;
541 };
542 const is_via_got = relocRequiresGot(zld, rel);
543 const is_tlv = is_tlv: {
544 const source_sym = zld.getSymbol(atom.getSymbolWithLoc());
545 const header = zld.sections.items(.header)[source_sym.n_sect - 1];
546 break :is_tlv header.type() == macho.S_THREAD_LOCAL_VARIABLES;
547 };
548 const target_addr = try getRelocTargetAddress(zld, target, is_via_got, is_tlv);
549
550 log.debug(" | source_addr = 0x{x}", .{source_addr});
551
552 switch (rel_type) {
553 .ARM64_RELOC_BRANCH26 => {
554 const actual_target = if (zld.getStubsAtomIndexForSymbol(target)) |stub_atom_index| inner: {
555 const stub_atom = zld.getAtom(stub_atom_index);
556 break :inner stub_atom.getSymbolWithLoc();
557 } else target;
558 log.debug(" source {s} (object({?})), target {s} (object({?}))", .{
559 zld.getSymbolName(atom.getSymbolWithLoc()),
560 atom.getFile(),
561 zld.getSymbolName(target),
562 zld.getAtom(getRelocTargetAtomIndex(zld, target, is_via_got).?).getFile(),
563 });
564
565 const displacement = if (Relocation.calcPcRelativeDisplacementArm64(
566 source_addr,
567 zld.getSymbol(actual_target).n_value,
568 )) |disp| blk: {
569 log.debug(" | target_addr = 0x{x}", .{zld.getSymbol(actual_target).n_value});
570 break :blk disp;
571 } else |_| blk: {
572 const thunk_index = zld.thunk_table.get(atom_index).?;
573 const thunk = zld.thunks.items[thunk_index];
574 const thunk_sym = zld.getSymbol(thunk.getTrampolineForSymbol(
575 zld,
576 actual_target,
577 ).?);
578 log.debug(" | target_addr = 0x{x} (thunk)", .{thunk_sym.n_value});
579 break :blk try Relocation.calcPcRelativeDisplacementArm64(source_addr, thunk_sym.n_value);
580 };
581
582 const code = atom_code[rel_offset..][0..4];
583 var inst = aarch64.Instruction{
584 .unconditional_branch_immediate = mem.bytesToValue(meta.TagPayload(
585 aarch64.Instruction,
586 aarch64.Instruction.unconditional_branch_immediate,
587 ), code),
588 };
589 inst.unconditional_branch_immediate.imm26 = @as(u26, @truncate(@as(u28, @bitCast(displacement >> 2))));
590 mem.writeIntLittle(u32, code, inst.toU32());
591 },
592
593 .ARM64_RELOC_PAGE21,
594 .ARM64_RELOC_GOT_LOAD_PAGE21,
595 .ARM64_RELOC_TLVP_LOAD_PAGE21,
596 => {
597 const adjusted_target_addr = @as(u64, @intCast(@as(i64, @intCast(target_addr)) + (addend orelse 0)));
598
599 log.debug(" | target_addr = 0x{x}", .{adjusted_target_addr});
600
601 const pages = @as(u21, @bitCast(Relocation.calcNumberOfPages(source_addr, adjusted_target_addr)));
602 const code = atom_code[rel_offset..][0..4];
603 var inst = aarch64.Instruction{
604 .pc_relative_address = mem.bytesToValue(meta.TagPayload(
605 aarch64.Instruction,
606 aarch64.Instruction.pc_relative_address,
607 ), code),
608 };
609 inst.pc_relative_address.immhi = @as(u19, @truncate(pages >> 2));
610 inst.pc_relative_address.immlo = @as(u2, @truncate(pages));
611 mem.writeIntLittle(u32, code, inst.toU32());
612 addend = null;
613 },
614
615 .ARM64_RELOC_PAGEOFF12 => {
616 const adjusted_target_addr = @as(u64, @intCast(@as(i64, @intCast(target_addr)) + (addend orelse 0)));
617
618 log.debug(" | target_addr = 0x{x}", .{adjusted_target_addr});
619
620 const code = atom_code[rel_offset..][0..4];
621 if (Relocation.isArithmeticOp(code)) {
622 const off = try Relocation.calcPageOffset(adjusted_target_addr, .arithmetic);
623 var inst = aarch64.Instruction{
624 .add_subtract_immediate = mem.bytesToValue(meta.TagPayload(
625 aarch64.Instruction,
626 aarch64.Instruction.add_subtract_immediate,
627 ), code),
628 };
629 inst.add_subtract_immediate.imm12 = off;
630 mem.writeIntLittle(u32, code, inst.toU32());
631 } else {
632 var inst = aarch64.Instruction{
633 .load_store_register = mem.bytesToValue(meta.TagPayload(
634 aarch64.Instruction,
635 aarch64.Instruction.load_store_register,
636 ), code),
637 };
638 const off = try Relocation.calcPageOffset(adjusted_target_addr, switch (inst.load_store_register.size) {
639 0 => if (inst.load_store_register.v == 1)
640 Relocation.PageOffsetInstKind.load_store_128
641 else
642 Relocation.PageOffsetInstKind.load_store_8,
643 1 => .load_store_16,
644 2 => .load_store_32,
645 3 => .load_store_64,
646 });
647 inst.load_store_register.offset = off;
648 mem.writeIntLittle(u32, code, inst.toU32());
649 }
650 addend = null;
651 },
652
653 .ARM64_RELOC_GOT_LOAD_PAGEOFF12 => {
654 const code = atom_code[rel_offset..][0..4];
655 const adjusted_target_addr = @as(u64, @intCast(@as(i64, @intCast(target_addr)) + (addend orelse 0)));
656
657 log.debug(" | target_addr = 0x{x}", .{adjusted_target_addr});
658
659 const off = try Relocation.calcPageOffset(adjusted_target_addr, .load_store_64);
660 var inst: aarch64.Instruction = .{
661 .load_store_register = mem.bytesToValue(meta.TagPayload(
662 aarch64.Instruction,
663 aarch64.Instruction.load_store_register,
664 ), code),
665 };
666 inst.load_store_register.offset = off;
667 mem.writeIntLittle(u32, code, inst.toU32());
668 addend = null;
669 },
670
671 .ARM64_RELOC_TLVP_LOAD_PAGEOFF12 => {
672 const code = atom_code[rel_offset..][0..4];
673 const adjusted_target_addr = @as(u64, @intCast(@as(i64, @intCast(target_addr)) + (addend orelse 0)));
674
675 log.debug(" | target_addr = 0x{x}", .{adjusted_target_addr});
676
677 const RegInfo = struct {
678 rd: u5,
679 rn: u5,
680 size: u2,
681 };
682 const reg_info: RegInfo = blk: {
683 if (Relocation.isArithmeticOp(code)) {
684 const inst = mem.bytesToValue(meta.TagPayload(
685 aarch64.Instruction,
686 aarch64.Instruction.add_subtract_immediate,
687 ), code);
688 break :blk .{
689 .rd = inst.rd,
690 .rn = inst.rn,
691 .size = inst.sf,
692 };
693 } else {
694 const inst = mem.bytesToValue(meta.TagPayload(
695 aarch64.Instruction,
696 aarch64.Instruction.load_store_register,
697 ), code);
698 break :blk .{
699 .rd = inst.rt,
700 .rn = inst.rn,
701 .size = inst.size,
702 };
703 }
704 };
705
706 var inst = if (zld.tlv_ptr_table.contains(target)) aarch64.Instruction{
707 .load_store_register = .{
708 .rt = reg_info.rd,
709 .rn = reg_info.rn,
710 .offset = try Relocation.calcPageOffset(adjusted_target_addr, .load_store_64),
711 .opc = 0b01,
712 .op1 = 0b01,
713 .v = 0,
714 .size = reg_info.size,
715 },
716 } else aarch64.Instruction{
717 .add_subtract_immediate = .{
718 .rd = reg_info.rd,
719 .rn = reg_info.rn,
720 .imm12 = try Relocation.calcPageOffset(adjusted_target_addr, .arithmetic),
721 .sh = 0,
722 .s = 0,
723 .op = 0,
724 .sf = @as(u1, @truncate(reg_info.size)),
725 },
726 };
727 mem.writeIntLittle(u32, code, inst.toU32());
728 addend = null;
729 },
730
731 .ARM64_RELOC_POINTER_TO_GOT => {
732 log.debug(" | target_addr = 0x{x}", .{target_addr});
733 const result = math.cast(i32, @as(i64, @intCast(target_addr)) - @as(i64, @intCast(source_addr))) orelse
734 return error.Overflow;
735 mem.writeIntLittle(u32, atom_code[rel_offset..][0..4], @as(u32, @bitCast(result)));
736 },
737
738 .ARM64_RELOC_UNSIGNED => {
739 var ptr_addend = if (rel.r_length == 3)
740 mem.readIntLittle(i64, atom_code[rel_offset..][0..8])
741 else
742 mem.readIntLittle(i32, atom_code[rel_offset..][0..4]);
743
744 if (rel.r_extern == 0) {
745 const base_addr = if (target.sym_index >= object.source_address_lookup.len)
746 @as(i64, @intCast(object.getSourceSection(@as(u8, @intCast(rel.r_symbolnum - 1))).addr))
747 else
748 object.source_address_lookup[target.sym_index];
749 ptr_addend -= base_addr;
750 }
751
752 const result = blk: {
753 if (subtractor) |sub| {
754 const sym = zld.getSymbol(sub);
755 break :blk @as(i64, @intCast(target_addr)) - @as(i64, @intCast(sym.n_value)) + ptr_addend;
756 } else {
757 break :blk @as(i64, @intCast(target_addr)) + ptr_addend;
758 }
759 };
760 log.debug(" | target_addr = 0x{x}", .{result});
761
762 if (rel.r_length == 3) {
763 mem.writeIntLittle(u64, atom_code[rel_offset..][0..8], @as(u64, @bitCast(result)));
764 } else {
765 mem.writeIntLittle(u32, atom_code[rel_offset..][0..4], @as(u32, @truncate(@as(u64, @bitCast(result)))));
766 }
767
768 subtractor = null;
769 },
770
771 .ARM64_RELOC_ADDEND => unreachable,
772 .ARM64_RELOC_SUBTRACTOR => unreachable,
773 }
774 }
775}
776
777fn resolveRelocsX86(
778 zld: *Zld,
779 atom_index: AtomIndex,
780 atom_code: []u8,
781 atom_relocs: []align(1) const macho.relocation_info,
782 context: RelocContext,
783) !void {
784 const atom = zld.getAtom(atom_index);
785 const object = zld.objects.items[atom.getFile().?];
786
787 var subtractor: ?SymbolWithLoc = null;
788
789 for (atom_relocs) |rel| {
790 const rel_type = @as(macho.reloc_type_x86_64, @enumFromInt(rel.r_type));
791
792 switch (rel_type) {
793 .X86_64_RELOC_SUBTRACTOR => {
794 assert(subtractor == null);
795
796 log.debug(" RELA({s}) @ {x} => %{d} in object({?d})", .{
797 @tagName(rel_type),
798 rel.r_address,
799 rel.r_symbolnum,
800 atom.getFile(),
801 });
802
803 subtractor = parseRelocTarget(zld, .{
804 .object_id = atom.getFile().?,
805 .rel = rel,
806 .code = atom_code,
807 .base_addr = context.base_addr,
808 .base_offset = context.base_offset,
809 });
810 continue;
811 },
812 else => {},
813 }
814
815 const target = parseRelocTarget(zld, .{
816 .object_id = atom.getFile().?,
817 .rel = rel,
818 .code = atom_code,
819 .base_addr = context.base_addr,
820 .base_offset = context.base_offset,
821 });
822 const rel_offset = @as(u32, @intCast(rel.r_address - context.base_offset));
823
824 log.debug(" RELA({s}) @ {x} => %{d} ('{s}') in object({?})", .{
825 @tagName(rel_type),
826 rel.r_address,
827 target.sym_index,
828 zld.getSymbolName(target),
829 target.getFile(),
830 });
831
832 const source_addr = blk: {
833 const source_sym = zld.getSymbol(atom.getSymbolWithLoc());
834 break :blk source_sym.n_value + rel_offset;
835 };
836 const is_via_got = relocRequiresGot(zld, rel);
837 const is_tlv = is_tlv: {
838 const source_sym = zld.getSymbol(atom.getSymbolWithLoc());
839 const header = zld.sections.items(.header)[source_sym.n_sect - 1];
840 break :is_tlv header.type() == macho.S_THREAD_LOCAL_VARIABLES;
841 };
842
843 log.debug(" | source_addr = 0x{x}", .{source_addr});
844
845 const target_addr = try getRelocTargetAddress(zld, target, is_via_got, is_tlv);
846
847 switch (rel_type) {
848 .X86_64_RELOC_BRANCH => {
849 const addend = mem.readIntLittle(i32, atom_code[rel_offset..][0..4]);
850 const adjusted_target_addr = @as(u64, @intCast(@as(i64, @intCast(target_addr)) + addend));
851 log.debug(" | target_addr = 0x{x}", .{adjusted_target_addr});
852 const disp = try Relocation.calcPcRelativeDisplacementX86(source_addr, adjusted_target_addr, 0);
853 mem.writeIntLittle(i32, atom_code[rel_offset..][0..4], disp);
854 },
855
856 .X86_64_RELOC_GOT,
857 .X86_64_RELOC_GOT_LOAD,
858 => {
859 const addend = mem.readIntLittle(i32, atom_code[rel_offset..][0..4]);
860 const adjusted_target_addr = @as(u64, @intCast(@as(i64, @intCast(target_addr)) + addend));
861 log.debug(" | target_addr = 0x{x}", .{adjusted_target_addr});
862 const disp = try Relocation.calcPcRelativeDisplacementX86(source_addr, adjusted_target_addr, 0);
863 mem.writeIntLittle(i32, atom_code[rel_offset..][0..4], disp);
864 },
865
866 .X86_64_RELOC_TLV => {
867 const addend = mem.readIntLittle(i32, atom_code[rel_offset..][0..4]);
868 const adjusted_target_addr = @as(u64, @intCast(@as(i64, @intCast(target_addr)) + addend));
869 log.debug(" | target_addr = 0x{x}", .{adjusted_target_addr});
870 const disp = try Relocation.calcPcRelativeDisplacementX86(source_addr, adjusted_target_addr, 0);
871
872 if (zld.tlv_ptr_table.get(target) == null) {
873 // We need to rewrite the opcode from movq to leaq.
874 atom_code[rel_offset - 2] = 0x8d;
875 }
876
877 mem.writeIntLittle(i32, atom_code[rel_offset..][0..4], disp);
878 },
879
880 .X86_64_RELOC_SIGNED,
881 .X86_64_RELOC_SIGNED_1,
882 .X86_64_RELOC_SIGNED_2,
883 .X86_64_RELOC_SIGNED_4,
884 => {
885 const correction: u3 = switch (rel_type) {
886 .X86_64_RELOC_SIGNED => 0,
887 .X86_64_RELOC_SIGNED_1 => 1,
888 .X86_64_RELOC_SIGNED_2 => 2,
889 .X86_64_RELOC_SIGNED_4 => 4,
890 else => unreachable,
891 };
892 var addend = mem.readIntLittle(i32, atom_code[rel_offset..][0..4]) + correction;
893
894 if (rel.r_extern == 0) {
895 const base_addr = if (target.sym_index >= object.source_address_lookup.len)
896 @as(i64, @intCast(object.getSourceSection(@as(u8, @intCast(rel.r_symbolnum - 1))).addr))
897 else
898 object.source_address_lookup[target.sym_index];
899 addend += @as(i32, @intCast(@as(i64, @intCast(context.base_addr)) + rel.r_address + 4 -
900 @as(i64, @intCast(base_addr))));
901 }
902
903 const adjusted_target_addr = @as(u64, @intCast(@as(i64, @intCast(target_addr)) + addend));
904
905 log.debug(" | target_addr = 0x{x}", .{adjusted_target_addr});
906
907 const disp = try Relocation.calcPcRelativeDisplacementX86(source_addr, adjusted_target_addr, correction);
908 mem.writeIntLittle(i32, atom_code[rel_offset..][0..4], disp);
909 },
910
911 .X86_64_RELOC_UNSIGNED => {
912 var addend = if (rel.r_length == 3)
913 mem.readIntLittle(i64, atom_code[rel_offset..][0..8])
914 else
915 mem.readIntLittle(i32, atom_code[rel_offset..][0..4]);
916
917 if (rel.r_extern == 0) {
918 const base_addr = if (target.sym_index >= object.source_address_lookup.len)
919 @as(i64, @intCast(object.getSourceSection(@as(u8, @intCast(rel.r_symbolnum - 1))).addr))
920 else
921 object.source_address_lookup[target.sym_index];
922 addend -= base_addr;
923 }
924
925 const result = blk: {
926 if (subtractor) |sub| {
927 const sym = zld.getSymbol(sub);
928 break :blk @as(i64, @intCast(target_addr)) - @as(i64, @intCast(sym.n_value)) + addend;
929 } else {
930 break :blk @as(i64, @intCast(target_addr)) + addend;
931 }
932 };
933 log.debug(" | target_addr = 0x{x}", .{result});
934
935 if (rel.r_length == 3) {
936 mem.writeIntLittle(u64, atom_code[rel_offset..][0..8], @as(u64, @bitCast(result)));
937 } else {
938 mem.writeIntLittle(u32, atom_code[rel_offset..][0..4], @as(u32, @truncate(@as(u64, @bitCast(result)))));
939 }
940
941 subtractor = null;
942 },
943
944 .X86_64_RELOC_SUBTRACTOR => unreachable,
945 }
946 }
947}
948
949pub fn getAtomCode(zld: *Zld, atom_index: AtomIndex) []const u8 {
950 const atom = zld.getAtom(atom_index);
951 assert(atom.getFile() != null); // Synthetic atom shouldn't need to inquire for code.
952 const object = zld.objects.items[atom.getFile().?];
953 const source_sym = object.getSourceSymbol(atom.sym_index) orelse {
954 // If there was no matching symbol present in the source symtab, this means
955 // we are dealing with either an entire section, or part of it, but also
956 // starting at the beginning.
957 const nbase = @as(u32, @intCast(object.in_symtab.?.len));
958 const sect_id = @as(u8, @intCast(atom.sym_index - nbase));
959 const source_sect = object.getSourceSection(sect_id);
960 assert(!source_sect.isZerofill());
961 const code = object.getSectionContents(source_sect);
962 const code_len = @as(usize, @intCast(atom.size));
963 return code[0..code_len];
964 };
965 const source_sect = object.getSourceSection(source_sym.n_sect - 1);
966 assert(!source_sect.isZerofill());
967 const code = object.getSectionContents(source_sect);
968 const offset = @as(usize, @intCast(source_sym.n_value - source_sect.addr));
969 const code_len = @as(usize, @intCast(atom.size));
970 return code[offset..][0..code_len];
971}
972
973pub fn getAtomRelocs(zld: *Zld, atom_index: AtomIndex) []const macho.relocation_info {
974 const atom = zld.getAtom(atom_index);
975 assert(atom.getFile() != null); // Synthetic atom shouldn't need to unique for relocs.
976 const object = zld.objects.items[atom.getFile().?];
977 const cache = object.relocs_lookup[atom.sym_index];
978
979 const source_sect_id = if (object.getSourceSymbol(atom.sym_index)) |source_sym| blk: {
980 break :blk source_sym.n_sect - 1;
981 } else blk: {
982 // If there was no matching symbol present in the source symtab, this means
983 // we are dealing with either an entire section, or part of it, but also
984 // starting at the beginning.
985 const nbase = @as(u32, @intCast(object.in_symtab.?.len));
986 const sect_id = @as(u8, @intCast(atom.sym_index - nbase));
987 break :blk sect_id;
988 };
989 const source_sect = object.getSourceSection(source_sect_id);
990 assert(!source_sect.isZerofill());
991 const relocs = object.getRelocs(source_sect_id);
992 return relocs[cache.start..][0..cache.len];
993}
994
995pub fn relocRequiresGot(zld: *Zld, rel: macho.relocation_info) bool {
996 switch (zld.options.target.cpu.arch) {
997 .aarch64 => switch (@as(macho.reloc_type_arm64, @enumFromInt(rel.r_type))) {
998 .ARM64_RELOC_GOT_LOAD_PAGE21,
999 .ARM64_RELOC_GOT_LOAD_PAGEOFF12,
1000 .ARM64_RELOC_POINTER_TO_GOT,
1001 => return true,
1002 else => return false,
1003 },
1004 .x86_64 => switch (@as(macho.reloc_type_x86_64, @enumFromInt(rel.r_type))) {
1005 .X86_64_RELOC_GOT,
1006 .X86_64_RELOC_GOT_LOAD,
1007 => return true,
1008 else => return false,
1009 },
1010 else => unreachable,
1011 }
1012}
src/link/MachO/dead_strip.zig+137-140
......@@ -1,92 +1,73 @@
11//! An algorithm for dead stripping of unreferenced Atoms.
22
3const std = @import("std");
4const assert = std.debug.assert;
5const eh_frame = @import("eh_frame.zig");
6const log = std.log.scoped(.dead_strip);
7const macho = std.macho;
8const math = std.math;
9const mem = std.mem;
10
11const Allocator = mem.Allocator;
12const AtomIndex = @import("zld.zig").AtomIndex;
13const Atom = @import("ZldAtom.zig");
14const SymbolWithLoc = @import("zld.zig").SymbolWithLoc;
15const SymbolResolver = @import("zld.zig").SymbolResolver;
16const UnwindInfo = @import("UnwindInfo.zig");
17const Zld = @import("zld.zig").Zld;
18
19const N_DEAD = @import("zld.zig").N_DEAD;
20
21const AtomTable = std.AutoHashMap(AtomIndex, void);
22
23pub fn gcAtoms(zld: *Zld, resolver: *const SymbolResolver) !void {
24 const gpa = zld.gpa;
3pub fn gcAtoms(macho_file: *MachO) !void {
4 const gpa = macho_file.base.allocator;
255
266 var arena = std.heap.ArenaAllocator.init(gpa);
277 defer arena.deinit();
288
299 var roots = AtomTable.init(arena.allocator());
30 try roots.ensureUnusedCapacity(@as(u32, @intCast(zld.globals.items.len)));
10 try roots.ensureUnusedCapacity(@as(u32, @intCast(macho_file.globals.items.len)));
3111
3212 var alive = AtomTable.init(arena.allocator());
33 try alive.ensureTotalCapacity(@as(u32, @intCast(zld.atoms.items.len)));
13 try alive.ensureTotalCapacity(@as(u32, @intCast(macho_file.atoms.items.len)));
3414
35 try collectRoots(zld, &roots, resolver);
36 try mark(zld, roots, &alive);
37 prune(zld, alive);
15 try collectRoots(macho_file, &roots);
16 mark(macho_file, roots, &alive);
17 prune(macho_file, alive);
3818}
3919
40fn addRoot(zld: *Zld, roots: *AtomTable, file: u32, sym_loc: SymbolWithLoc) !void {
41 const sym = zld.getSymbol(sym_loc);
20fn addRoot(macho_file: *MachO, roots: *AtomTable, file: u32, sym_loc: SymbolWithLoc) !void {
21 const sym = macho_file.getSymbol(sym_loc);
4222 assert(!sym.undf());
43 const object = &zld.objects.items[file];
23 const object = &macho_file.objects.items[file];
4424 const atom_index = object.getAtomIndexForSymbol(sym_loc.sym_index).?; // panic here means fatal error
4525 log.debug("root(ATOM({d}, %{d}, {d}))", .{
4626 atom_index,
47 zld.getAtom(atom_index).sym_index,
27 macho_file.getAtom(atom_index).sym_index,
4828 file,
4929 });
5030 _ = try roots.getOrPut(atom_index);
5131}
5232
53fn collectRoots(zld: *Zld, roots: *AtomTable, resolver: *const SymbolResolver) !void {
33fn collectRoots(macho_file: *MachO, roots: *AtomTable) !void {
5434 log.debug("collecting roots", .{});
5535
56 switch (zld.options.output_mode) {
36 switch (macho_file.base.options.output_mode) {
5737 .Exe => {
5838 // Add entrypoint as GC root
59 const global: SymbolWithLoc = zld.getEntryPoint();
60 if (global.getFile()) |file| {
61 try addRoot(zld, roots, file, global);
62 } else {
63 assert(zld.getSymbol(global).undf()); // Stub as our entrypoint is in a dylib.
39 if (macho_file.getEntryPoint()) |global| {
40 if (global.getFile()) |file| {
41 try addRoot(macho_file, roots, file, global);
42 } else {
43 assert(macho_file.getSymbol(global).undf()); // Stub as our entrypoint is in a dylib.
44 }
6445 }
6546 },
6647 else => |other| {
6748 assert(other == .Lib);
6849 // Add exports as GC roots
69 for (zld.globals.items) |global| {
70 const sym = zld.getSymbol(global);
50 for (macho_file.globals.items) |global| {
51 const sym = macho_file.getSymbol(global);
7152 if (sym.undf()) continue;
7253
7354 if (global.getFile()) |file| {
74 try addRoot(zld, roots, file, global);
55 try addRoot(macho_file, roots, file, global);
7556 }
7657 }
7758 },
7859 }
7960
8061 // Add all symbols force-defined by the user.
81 for (zld.options.force_undefined_symbols.keys()) |sym_name| {
82 const global_index = resolver.table.get(sym_name).?;
83 const global = zld.globals.items[global_index];
84 const sym = zld.getSymbol(global);
62 for (macho_file.base.options.force_undefined_symbols.keys()) |sym_name| {
63 const global_index = macho_file.resolver.get(sym_name).?;
64 const global = macho_file.globals.items[global_index];
65 const sym = macho_file.getSymbol(global);
8566 assert(!sym.undf());
86 try addRoot(zld, roots, global.getFile().?, global);
67 try addRoot(macho_file, roots, global.getFile().?, global);
8768 }
8869
89 for (zld.objects.items) |object| {
70 for (macho_file.objects.items) |object| {
9071 const has_subsections = object.header.flags & macho.MH_SUBSECTIONS_VIA_SYMBOLS != 0;
9172
9273 for (object.atoms.items) |atom_index| {
......@@ -95,7 +76,7 @@ fn collectRoots(zld: *Zld, roots: *AtomTable, resolver: *const SymbolResolver) !
9576 // as a root.
9677 if (!has_subsections) break :blk true;
9778
98 const atom = zld.getAtom(atom_index);
79 const atom = macho_file.getAtom(atom_index);
9980 const sect_id = if (object.getSourceSymbol(atom.sym_index)) |source_sym|
10081 source_sym.n_sect - 1
10182 else sect_id: {
......@@ -118,39 +99,39 @@ fn collectRoots(zld: *Zld, roots: *AtomTable, resolver: *const SymbolResolver) !
11899
119100 log.debug("root(ATOM({d}, %{d}, {?d}))", .{
120101 atom_index,
121 zld.getAtom(atom_index).sym_index,
122 zld.getAtom(atom_index).getFile(),
102 macho_file.getAtom(atom_index).sym_index,
103 macho_file.getAtom(atom_index).getFile(),
123104 });
124105 }
125106 }
126107 }
127108}
128109
129fn markLive(zld: *Zld, atom_index: AtomIndex, alive: *AtomTable) void {
110fn markLive(macho_file: *MachO, atom_index: Atom.Index, alive: *AtomTable) void {
130111 if (alive.contains(atom_index)) return;
131112
132 const atom = zld.getAtom(atom_index);
113 const atom = macho_file.getAtom(atom_index);
133114 const sym_loc = atom.getSymbolWithLoc();
134115
135116 log.debug("mark(ATOM({d}, %{d}, {?d}))", .{ atom_index, sym_loc.sym_index, sym_loc.getFile() });
136117
137118 alive.putAssumeCapacityNoClobber(atom_index, {});
138119
139 const cpu_arch = zld.options.target.cpu.arch;
120 const cpu_arch = macho_file.base.options.target.cpu.arch;
140121
141 const sym = zld.getSymbol(atom.getSymbolWithLoc());
142 const header = zld.sections.items(.header)[sym.n_sect - 1];
122 const sym = macho_file.getSymbol(atom.getSymbolWithLoc());
123 const header = macho_file.sections.items(.header)[sym.n_sect - 1];
143124 if (header.isZerofill()) return;
144125
145 const code = Atom.getAtomCode(zld, atom_index);
146 const relocs = Atom.getAtomRelocs(zld, atom_index);
147 const ctx = Atom.getRelocContext(zld, atom_index);
126 const code = Atom.getAtomCode(macho_file, atom_index);
127 const relocs = Atom.getAtomRelocs(macho_file, atom_index);
128 const ctx = Atom.getRelocContext(macho_file, atom_index);
148129
149130 for (relocs) |rel| {
150131 const target = switch (cpu_arch) {
151132 .aarch64 => switch (@as(macho.reloc_type_arm64, @enumFromInt(rel.r_type))) {
152133 .ARM64_RELOC_ADDEND => continue,
153 else => Atom.parseRelocTarget(zld, .{
134 else => Atom.parseRelocTarget(macho_file, .{
154135 .object_id = atom.getFile().?,
155136 .rel = rel,
156137 .code = code,
......@@ -158,7 +139,7 @@ fn markLive(zld: *Zld, atom_index: AtomIndex, alive: *AtomTable) void {
158139 .base_addr = ctx.base_addr,
159140 }),
160141 },
161 .x86_64 => Atom.parseRelocTarget(zld, .{
142 .x86_64 => Atom.parseRelocTarget(macho_file, .{
162143 .object_id = atom.getFile().?,
163144 .rel = rel,
164145 .code = code,
......@@ -167,50 +148,50 @@ fn markLive(zld: *Zld, atom_index: AtomIndex, alive: *AtomTable) void {
167148 }),
168149 else => unreachable,
169150 };
170 const target_sym = zld.getSymbol(target);
151 const target_sym = macho_file.getSymbol(target);
171152
172153 if (target_sym.undf()) continue;
173154 if (target.getFile() == null) {
174 const target_sym_name = zld.getSymbolName(target);
155 const target_sym_name = macho_file.getSymbolName(target);
175156 if (mem.eql(u8, "__mh_execute_header", target_sym_name)) continue;
176157 if (mem.eql(u8, "___dso_handle", target_sym_name)) continue;
177158
178159 unreachable; // referenced symbol not found
179160 }
180161
181 const object = zld.objects.items[target.getFile().?];
162 const object = macho_file.objects.items[target.getFile().?];
182163 const target_atom_index = object.getAtomIndexForSymbol(target.sym_index).?;
183164 log.debug(" following ATOM({d}, %{d}, {?d})", .{
184165 target_atom_index,
185 zld.getAtom(target_atom_index).sym_index,
186 zld.getAtom(target_atom_index).getFile(),
166 macho_file.getAtom(target_atom_index).sym_index,
167 macho_file.getAtom(target_atom_index).getFile(),
187168 });
188169
189 markLive(zld, target_atom_index, alive);
170 markLive(macho_file, target_atom_index, alive);
190171 }
191172}
192173
193fn refersLive(zld: *Zld, atom_index: AtomIndex, alive: AtomTable) bool {
194 const atom = zld.getAtom(atom_index);
174fn refersLive(macho_file: *MachO, atom_index: Atom.Index, alive: AtomTable) bool {
175 const atom = macho_file.getAtom(atom_index);
195176 const sym_loc = atom.getSymbolWithLoc();
196177
197178 log.debug("refersLive(ATOM({d}, %{d}, {?d}))", .{ atom_index, sym_loc.sym_index, sym_loc.getFile() });
198179
199 const cpu_arch = zld.options.target.cpu.arch;
180 const cpu_arch = macho_file.base.options.target.cpu.arch;
200181
201 const sym = zld.getSymbol(sym_loc);
202 const header = zld.sections.items(.header)[sym.n_sect - 1];
182 const sym = macho_file.getSymbol(sym_loc);
183 const header = macho_file.sections.items(.header)[sym.n_sect - 1];
203184 assert(!header.isZerofill());
204185
205 const code = Atom.getAtomCode(zld, atom_index);
206 const relocs = Atom.getAtomRelocs(zld, atom_index);
207 const ctx = Atom.getRelocContext(zld, atom_index);
186 const code = Atom.getAtomCode(macho_file, atom_index);
187 const relocs = Atom.getAtomRelocs(macho_file, atom_index);
188 const ctx = Atom.getRelocContext(macho_file, atom_index);
208189
209190 for (relocs) |rel| {
210191 const target = switch (cpu_arch) {
211192 .aarch64 => switch (@as(macho.reloc_type_arm64, @enumFromInt(rel.r_type))) {
212193 .ARM64_RELOC_ADDEND => continue,
213 else => Atom.parseRelocTarget(zld, .{
194 else => Atom.parseRelocTarget(macho_file, .{
214195 .object_id = atom.getFile().?,
215196 .rel = rel,
216197 .code = code,
......@@ -218,7 +199,7 @@ fn refersLive(zld: *Zld, atom_index: AtomIndex, alive: AtomTable) bool {
218199 .base_addr = ctx.base_addr,
219200 }),
220201 },
221 .x86_64 => Atom.parseRelocTarget(zld, .{
202 .x86_64 => Atom.parseRelocTarget(macho_file, .{
222203 .object_id = atom.getFile().?,
223204 .rel = rel,
224205 .code = code,
......@@ -228,16 +209,16 @@ fn refersLive(zld: *Zld, atom_index: AtomIndex, alive: AtomTable) bool {
228209 else => unreachable,
229210 };
230211
231 const object = zld.objects.items[target.getFile().?];
212 const object = macho_file.objects.items[target.getFile().?];
232213 const target_atom_index = object.getAtomIndexForSymbol(target.sym_index) orelse {
233 log.debug("atom for symbol '{s}' not found; skipping...", .{zld.getSymbolName(target)});
214 log.debug("atom for symbol '{s}' not found; skipping...", .{macho_file.getSymbolName(target)});
234215 continue;
235216 };
236217 if (alive.contains(target_atom_index)) {
237218 log.debug(" refers live ATOM({d}, %{d}, {?d})", .{
238219 target_atom_index,
239 zld.getAtom(target_atom_index).sym_index,
240 zld.getAtom(target_atom_index).getFile(),
220 macho_file.getAtom(target_atom_index).sym_index,
221 macho_file.getAtom(target_atom_index).getFile(),
241222 });
242223 return true;
243224 }
......@@ -246,21 +227,21 @@ fn refersLive(zld: *Zld, atom_index: AtomIndex, alive: AtomTable) bool {
246227 return false;
247228}
248229
249fn mark(zld: *Zld, roots: AtomTable, alive: *AtomTable) !void {
230fn mark(macho_file: *MachO, roots: AtomTable, alive: *AtomTable) void {
250231 var it = roots.keyIterator();
251232 while (it.next()) |root| {
252 markLive(zld, root.*, alive);
233 markLive(macho_file, root.*, alive);
253234 }
254235
255236 var loop: bool = true;
256237 while (loop) {
257238 loop = false;
258239
259 for (zld.objects.items) |object| {
240 for (macho_file.objects.items) |object| {
260241 for (object.atoms.items) |atom_index| {
261242 if (alive.contains(atom_index)) continue;
262243
263 const atom = zld.getAtom(atom_index);
244 const atom = macho_file.getAtom(atom_index);
264245 const sect_id = if (object.getSourceSymbol(atom.sym_index)) |source_sym|
265246 source_sym.n_sect - 1
266247 else blk: {
......@@ -271,8 +252,8 @@ fn mark(zld: *Zld, roots: AtomTable, alive: *AtomTable) !void {
271252 const source_sect = object.getSourceSection(sect_id);
272253
273254 if (source_sect.isDontDeadStripIfReferencesLive()) {
274 if (refersLive(zld, atom_index, alive.*)) {
275 markLive(zld, atom_index, alive);
255 if (refersLive(macho_file, atom_index, alive.*)) {
256 markLive(macho_file, atom_index, alive);
276257 loop = true;
277258 }
278259 }
......@@ -280,26 +261,26 @@ fn mark(zld: *Zld, roots: AtomTable, alive: *AtomTable) !void {
280261 }
281262 }
282263
283 for (zld.objects.items, 0..) |_, object_id| {
264 for (macho_file.objects.items, 0..) |_, object_id| {
284265 // Traverse unwind and eh_frame records noting if the source symbol has been marked, and if so,
285266 // marking all references as live.
286 try markUnwindRecords(zld, @as(u32, @intCast(object_id)), alive);
267 markUnwindRecords(macho_file, @as(u32, @intCast(object_id)), alive);
287268 }
288269}
289270
290fn markUnwindRecords(zld: *Zld, object_id: u32, alive: *AtomTable) !void {
291 const object = &zld.objects.items[object_id];
292 const cpu_arch = zld.options.target.cpu.arch;
271fn markUnwindRecords(macho_file: *MachO, object_id: u32, alive: *AtomTable) void {
272 const object = &macho_file.objects.items[object_id];
273 const cpu_arch = macho_file.base.options.target.cpu.arch;
293274
294275 const unwind_records = object.getUnwindRecords();
295276
296277 for (object.exec_atoms.items) |atom_index| {
297 var inner_syms_it = Atom.getInnerSymbolsIterator(zld, atom_index);
278 var inner_syms_it = Atom.getInnerSymbolsIterator(macho_file, atom_index);
298279
299280 if (!object.hasUnwindRecords()) {
300281 if (alive.contains(atom_index)) {
301282 // Mark references live and continue.
302 try markEhFrameRecords(zld, object_id, atom_index, alive);
283 markEhFrameRecords(macho_file, object_id, atom_index, alive);
303284 } else {
304285 while (inner_syms_it.next()) |sym| {
305286 if (object.eh_frame_records_lookup.get(sym)) |fde_offset| {
......@@ -325,86 +306,86 @@ fn markUnwindRecords(zld: *Zld, object_id: u32, alive: *AtomTable) !void {
325306
326307 const record = unwind_records[record_id];
327308 if (UnwindInfo.UnwindEncoding.isDwarf(record.compactUnwindEncoding, cpu_arch)) {
328 try markEhFrameRecords(zld, object_id, atom_index, alive);
309 markEhFrameRecords(macho_file, object_id, atom_index, alive);
329310 } else {
330 if (UnwindInfo.getPersonalityFunctionReloc(zld, object_id, record_id)) |rel| {
331 const target = Atom.parseRelocTarget(zld, .{
311 if (UnwindInfo.getPersonalityFunctionReloc(macho_file, object_id, record_id)) |rel| {
312 const target = Atom.parseRelocTarget(macho_file, .{
332313 .object_id = object_id,
333314 .rel = rel,
334315 .code = mem.asBytes(&record),
335316 .base_offset = @as(i32, @intCast(record_id * @sizeOf(macho.compact_unwind_entry))),
336317 });
337 const target_sym = zld.getSymbol(target);
318 const target_sym = macho_file.getSymbol(target);
338319 if (!target_sym.undf()) {
339 const target_object = zld.objects.items[target.getFile().?];
320 const target_object = macho_file.objects.items[target.getFile().?];
340321 const target_atom_index = target_object.getAtomIndexForSymbol(target.sym_index).?;
341 markLive(zld, target_atom_index, alive);
322 markLive(macho_file, target_atom_index, alive);
342323 }
343324 }
344325
345 if (UnwindInfo.getLsdaReloc(zld, object_id, record_id)) |rel| {
346 const target = Atom.parseRelocTarget(zld, .{
326 if (UnwindInfo.getLsdaReloc(macho_file, object_id, record_id)) |rel| {
327 const target = Atom.parseRelocTarget(macho_file, .{
347328 .object_id = object_id,
348329 .rel = rel,
349330 .code = mem.asBytes(&record),
350331 .base_offset = @as(i32, @intCast(record_id * @sizeOf(macho.compact_unwind_entry))),
351332 });
352 const target_object = zld.objects.items[target.getFile().?];
333 const target_object = macho_file.objects.items[target.getFile().?];
353334 const target_atom_index = target_object.getAtomIndexForSymbol(target.sym_index).?;
354 markLive(zld, target_atom_index, alive);
335 markLive(macho_file, target_atom_index, alive);
355336 }
356337 }
357338 }
358339 }
359340}
360341
361fn markEhFrameRecords(zld: *Zld, object_id: u32, atom_index: AtomIndex, alive: *AtomTable) !void {
362 const cpu_arch = zld.options.target.cpu.arch;
363 const object = &zld.objects.items[object_id];
342fn markEhFrameRecords(macho_file: *MachO, object_id: u32, atom_index: Atom.Index, alive: *AtomTable) void {
343 const cpu_arch = macho_file.base.options.target.cpu.arch;
344 const object = &macho_file.objects.items[object_id];
364345 var it = object.getEhFrameRecordsIterator();
365 var inner_syms_it = Atom.getInnerSymbolsIterator(zld, atom_index);
346 var inner_syms_it = Atom.getInnerSymbolsIterator(macho_file, atom_index);
366347
367348 while (inner_syms_it.next()) |sym| {
368349 const fde_offset = object.eh_frame_records_lookup.get(sym) orelse continue; // Continue in case we hit a temp symbol alias
369350 it.seekTo(fde_offset);
370 const fde = (try it.next()).?;
351 const fde = (it.next() catch continue).?; // We don't care about the error at this point since it was already handled
371352
372 const cie_ptr = fde.getCiePointerSource(object_id, zld, fde_offset);
353 const cie_ptr = fde.getCiePointerSource(object_id, macho_file, fde_offset);
373354 const cie_offset = fde_offset + 4 - cie_ptr;
374355 it.seekTo(cie_offset);
375 const cie = (try it.next()).?;
356 const cie = (it.next() catch continue).?; // We don't care about the error at this point since it was already handled
376357
377358 switch (cpu_arch) {
378359 .aarch64 => {
379360 // Mark FDE references which should include any referenced LSDA record
380 const relocs = eh_frame.getRelocs(zld, object_id, fde_offset);
361 const relocs = eh_frame.getRelocs(macho_file, object_id, fde_offset);
381362 for (relocs) |rel| {
382 const target = Atom.parseRelocTarget(zld, .{
363 const target = Atom.parseRelocTarget(macho_file, .{
383364 .object_id = object_id,
384365 .rel = rel,
385366 .code = fde.data,
386367 .base_offset = @as(i32, @intCast(fde_offset)) + 4,
387368 });
388 const target_sym = zld.getSymbol(target);
369 const target_sym = macho_file.getSymbol(target);
389370 if (!target_sym.undf()) blk: {
390 const target_object = zld.objects.items[target.getFile().?];
371 const target_object = macho_file.objects.items[target.getFile().?];
391372 const target_atom_index = target_object.getAtomIndexForSymbol(target.sym_index) orelse
392373 break :blk;
393 markLive(zld, target_atom_index, alive);
374 markLive(macho_file, target_atom_index, alive);
394375 }
395376 }
396377 },
397378 .x86_64 => {
398379 const sect = object.getSourceSection(object.eh_frame_sect_id.?);
399 const lsda_ptr = try fde.getLsdaPointer(cie, .{
380 const lsda_ptr = fde.getLsdaPointer(cie, .{
400381 .base_addr = sect.addr,
401382 .base_offset = fde_offset,
402 });
383 }) catch continue; // We don't care about the error at this point since it was already handled
403384 if (lsda_ptr) |lsda_address| {
404385 // Mark LSDA record as live
405386 const sym_index = object.getSymbolByAddress(lsda_address, null);
406387 const target_atom_index = object.getAtomIndexForSymbol(sym_index).?;
407 markLive(zld, target_atom_index, alive);
388 markLive(macho_file, target_atom_index, alive);
408389 }
409390 },
410391 else => unreachable,
......@@ -412,20 +393,20 @@ fn markEhFrameRecords(zld: *Zld, object_id: u32, atom_index: AtomIndex, alive: *
412393
413394 // Mark CIE references which should include any referenced personalities
414395 // that are defined locally.
415 if (cie.getPersonalityPointerReloc(zld, object_id, cie_offset)) |target| {
416 const target_sym = zld.getSymbol(target);
396 if (cie.getPersonalityPointerReloc(macho_file, object_id, cie_offset)) |target| {
397 const target_sym = macho_file.getSymbol(target);
417398 if (!target_sym.undf()) {
418 const target_object = zld.objects.items[target.getFile().?];
399 const target_object = macho_file.objects.items[target.getFile().?];
419400 const target_atom_index = target_object.getAtomIndexForSymbol(target.sym_index).?;
420 markLive(zld, target_atom_index, alive);
401 markLive(macho_file, target_atom_index, alive);
421402 }
422403 }
423404 }
424405}
425406
426fn prune(zld: *Zld, alive: AtomTable) void {
407fn prune(macho_file: *MachO, alive: AtomTable) void {
427408 log.debug("pruning dead atoms", .{});
428 for (zld.objects.items) |*object| {
409 for (macho_file.objects.items) |*object| {
429410 var i: usize = 0;
430411 while (i < object.atoms.items.len) {
431412 const atom_index = object.atoms.items[i];
......@@ -434,7 +415,7 @@ fn prune(zld: *Zld, alive: AtomTable) void {
434415 continue;
435416 }
436417
437 const atom = zld.getAtom(atom_index);
418 const atom = macho_file.getAtom(atom_index);
438419 const sym_loc = atom.getSymbolWithLoc();
439420
440421 log.debug("prune(ATOM({d}, %{d}, {?d}))", .{
......@@ -442,15 +423,15 @@ fn prune(zld: *Zld, alive: AtomTable) void {
442423 sym_loc.sym_index,
443424 sym_loc.getFile(),
444425 });
445 log.debug(" {s} in {s}", .{ zld.getSymbolName(sym_loc), object.name });
426 log.debug(" {s} in {s}", .{ macho_file.getSymbolName(sym_loc), object.name });
446427
447 const sym = zld.getSymbolPtr(sym_loc);
428 const sym = macho_file.getSymbolPtr(sym_loc);
448429 const sect_id = sym.n_sect - 1;
449 var section = zld.sections.get(sect_id);
430 var section = macho_file.sections.get(sect_id);
450431 section.header.size -= atom.size;
451432
452433 if (atom.prev_index) |prev_index| {
453 const prev = zld.getAtomPtr(prev_index);
434 const prev = macho_file.getAtomPtr(prev_index);
454435 prev.next_index = atom.next_index;
455436 } else {
456437 if (atom.next_index) |next_index| {
......@@ -458,33 +439,49 @@ fn prune(zld: *Zld, alive: AtomTable) void {
458439 }
459440 }
460441 if (atom.next_index) |next_index| {
461 const next = zld.getAtomPtr(next_index);
442 const next = macho_file.getAtomPtr(next_index);
462443 next.prev_index = atom.prev_index;
463444 } else {
464445 if (atom.prev_index) |prev_index| {
465446 section.last_atom_index = prev_index;
466447 } else {
467448 assert(section.header.size == 0);
468 section.first_atom_index = 0;
469 section.last_atom_index = 0;
449 section.first_atom_index = null;
450 section.last_atom_index = null;
470451 }
471452 }
472453
473 zld.sections.set(sect_id, section);
454 macho_file.sections.set(sect_id, section);
474455 _ = object.atoms.swapRemove(i);
475456
476 sym.n_desc = N_DEAD;
457 sym.n_desc = MachO.N_DEAD;
477458
478 var inner_sym_it = Atom.getInnerSymbolsIterator(zld, atom_index);
459 var inner_sym_it = Atom.getInnerSymbolsIterator(macho_file, atom_index);
479460 while (inner_sym_it.next()) |inner| {
480 const inner_sym = zld.getSymbolPtr(inner);
481 inner_sym.n_desc = N_DEAD;
461 const inner_sym = macho_file.getSymbolPtr(inner);
462 inner_sym.n_desc = MachO.N_DEAD;
482463 }
483464
484 if (Atom.getSectionAlias(zld, atom_index)) |alias| {
485 const alias_sym = zld.getSymbolPtr(alias);
486 alias_sym.n_desc = N_DEAD;
465 if (Atom.getSectionAlias(macho_file, atom_index)) |alias| {
466 const alias_sym = macho_file.getSymbolPtr(alias);
467 alias_sym.n_desc = MachO.N_DEAD;
487468 }
488469 }
489470 }
490471}
472
473const std = @import("std");
474const assert = std.debug.assert;
475const eh_frame = @import("eh_frame.zig");
476const log = std.log.scoped(.dead_strip);
477const macho = std.macho;
478const math = std.math;
479const mem = std.mem;
480
481const Allocator = mem.Allocator;
482const Atom = @import("Atom.zig");
483const MachO = @import("../MachO.zig");
484const SymbolWithLoc = MachO.SymbolWithLoc;
485const UnwindInfo = @import("UnwindInfo.zig");
486
487const AtomTable = std.AutoHashMap(Atom.Index, void);
src/link/MachO/dyld_info/Rebase.zig+11-11
......@@ -1,14 +1,3 @@
1const Rebase = @This();
2
3const std = @import("std");
4const assert = std.debug.assert;
5const leb = std.leb;
6const log = std.log.scoped(.dyld_info);
7const macho = std.macho;
8const testing = std.testing;
9
10const Allocator = std.mem.Allocator;
11
121entries: std.ArrayListUnmanaged(Entry) = .{},
132buffer: std.ArrayListUnmanaged(u8) = .{},
143
......@@ -572,3 +561,14 @@ test "rebase - composite" {
572561 macho.REBASE_OPCODE_DONE,
573562 }, rebase.buffer.items);
574563}
564
565const Rebase = @This();
566
567const std = @import("std");
568const assert = std.debug.assert;
569const leb = std.leb;
570const log = std.log.scoped(.dyld_info);
571const macho = std.macho;
572const testing = std.testing;
573
574const Allocator = std.mem.Allocator;
src/link/MachO/dyld_info/bind.zig+9-9
......@@ -1,12 +1,3 @@
1const std = @import("std");
2const assert = std.debug.assert;
3const leb = std.leb;
4const log = std.log.scoped(.dyld_info);
5const macho = std.macho;
6const testing = std.testing;
7
8const Allocator = std.mem.Allocator;
9
101pub fn Bind(comptime Ctx: type, comptime Target: type) type {
112 return struct {
123 entries: std.ArrayListUnmanaged(Entry) = .{},
......@@ -738,3 +729,12 @@ test "lazy bind" {
738729 macho.BIND_OPCODE_DONE,
739730 }, bind.buffer.items);
740731}
732
733const std = @import("std");
734const assert = std.debug.assert;
735const leb = std.leb;
736const log = std.log.scoped(.dyld_info);
737const macho = std.macho;
738const testing = std.testing;
739
740const Allocator = std.mem.Allocator;
src/link/MachO/eh_frame.zig+76-77
......@@ -1,68 +1,52 @@
1const std = @import("std");
2const assert = std.debug.assert;
3const macho = std.macho;
4const math = std.math;
5const mem = std.mem;
6const leb = std.leb;
7const log = std.log.scoped(.eh_frame);
1pub fn scanRelocs(macho_file: *MachO) !void {
2 const gpa = macho_file.base.allocator;
83
9const Allocator = mem.Allocator;
10const AtomIndex = @import("zld.zig").AtomIndex;
11const Atom = @import("ZldAtom.zig");
12const Relocation = @import("Relocation.zig");
13const SymbolWithLoc = @import("zld.zig").SymbolWithLoc;
14const UnwindInfo = @import("UnwindInfo.zig");
15const Zld = @import("zld.zig").Zld;
16
17pub fn scanRelocs(zld: *Zld) !void {
18 const gpa = zld.gpa;
19
20 for (zld.objects.items, 0..) |*object, object_id| {
4 for (macho_file.objects.items, 0..) |*object, object_id| {
215 var cies = std.AutoHashMap(u32, void).init(gpa);
226 defer cies.deinit();
237
248 var it = object.getEhFrameRecordsIterator();
259
2610 for (object.exec_atoms.items) |atom_index| {
27 var inner_syms_it = Atom.getInnerSymbolsIterator(zld, atom_index);
11 var inner_syms_it = Atom.getInnerSymbolsIterator(macho_file, atom_index);
2812 while (inner_syms_it.next()) |sym| {
2913 const fde_offset = object.eh_frame_records_lookup.get(sym) orelse continue;
3014 if (object.eh_frame_relocs_lookup.get(fde_offset).?.dead) continue;
3115 it.seekTo(fde_offset);
32 const fde = (try it.next()).?;
16 const fde = (it.next() catch continue).?; // We don't care about this error since we already handled it
3317
34 const cie_ptr = fde.getCiePointerSource(@intCast(object_id), zld, fde_offset);
18 const cie_ptr = fde.getCiePointerSource(@intCast(object_id), macho_file, fde_offset);
3519 const cie_offset = fde_offset + 4 - cie_ptr;
3620
3721 if (!cies.contains(cie_offset)) {
3822 try cies.putNoClobber(cie_offset, {});
3923 it.seekTo(cie_offset);
40 const cie = (try it.next()).?;
41 try cie.scanRelocs(zld, @as(u32, @intCast(object_id)), cie_offset);
24 const cie = (it.next() catch continue).?; // We don't care about this error since we already handled it
25 try cie.scanRelocs(macho_file, @as(u32, @intCast(object_id)), cie_offset);
4226 }
4327 }
4428 }
4529 }
4630}
4731
48pub fn calcSectionSize(zld: *Zld, unwind_info: *const UnwindInfo) !void {
49 const sect_id = zld.getSectionByName("__TEXT", "__eh_frame") orelse return;
50 const sect = &zld.sections.items(.header)[sect_id];
32pub fn calcSectionSize(macho_file: *MachO, unwind_info: *const UnwindInfo) error{OutOfMemory}!void {
33 const sect_id = macho_file.eh_frame_section_index orelse return;
34 const sect = &macho_file.sections.items(.header)[sect_id];
5135 sect.@"align" = 3;
5236 sect.size = 0;
5337
54 const cpu_arch = zld.options.target.cpu.arch;
55 const gpa = zld.gpa;
38 const cpu_arch = macho_file.base.options.target.cpu.arch;
39 const gpa = macho_file.base.allocator;
5640 var size: u32 = 0;
5741
58 for (zld.objects.items, 0..) |*object, object_id| {
42 for (macho_file.objects.items, 0..) |*object, object_id| {
5943 var cies = std.AutoHashMap(u32, u32).init(gpa);
6044 defer cies.deinit();
6145
6246 var eh_it = object.getEhFrameRecordsIterator();
6347
6448 for (object.exec_atoms.items) |atom_index| {
65 var inner_syms_it = Atom.getInnerSymbolsIterator(zld, atom_index);
49 var inner_syms_it = Atom.getInnerSymbolsIterator(macho_file, atom_index);
6650 while (inner_syms_it.next()) |sym| {
6751 const fde_record_offset = object.eh_frame_records_lookup.get(sym) orelse continue;
6852 if (object.eh_frame_relocs_lookup.get(fde_record_offset).?.dead) continue;
......@@ -75,15 +59,15 @@ pub fn calcSectionSize(zld: *Zld, unwind_info: *const UnwindInfo) !void {
7559 if (!is_dwarf) continue;
7660
7761 eh_it.seekTo(fde_record_offset);
78 const source_fde_record = (try eh_it.next()).?;
62 const source_fde_record = (eh_it.next() catch continue).?; // We already handled this error
7963
80 const cie_ptr = source_fde_record.getCiePointerSource(@intCast(object_id), zld, fde_record_offset);
64 const cie_ptr = source_fde_record.getCiePointerSource(@intCast(object_id), macho_file, fde_record_offset);
8165 const cie_offset = fde_record_offset + 4 - cie_ptr;
8266
8367 const gop = try cies.getOrPut(cie_offset);
8468 if (!gop.found_existing) {
8569 eh_it.seekTo(cie_offset);
86 const source_cie_record = (try eh_it.next()).?;
70 const source_cie_record = (eh_it.next() catch continue).?; // We already handled this error
8771 gop.value_ptr.* = size;
8872 size += source_cie_record.getSize();
8973 }
......@@ -96,14 +80,14 @@ pub fn calcSectionSize(zld: *Zld, unwind_info: *const UnwindInfo) !void {
9680 }
9781}
9882
99pub fn write(zld: *Zld, unwind_info: *UnwindInfo) !void {
100 const sect_id = zld.getSectionByName("__TEXT", "__eh_frame") orelse return;
101 const sect = zld.sections.items(.header)[sect_id];
102 const seg_id = zld.sections.items(.segment_index)[sect_id];
103 const seg = zld.segments.items[seg_id];
83pub fn write(macho_file: *MachO, unwind_info: *UnwindInfo) !void {
84 const sect_id = macho_file.eh_frame_section_index orelse return;
85 const sect = macho_file.sections.items(.header)[sect_id];
86 const seg_id = macho_file.sections.items(.segment_index)[sect_id];
87 const seg = macho_file.segments.items[seg_id];
10488
105 const cpu_arch = zld.options.target.cpu.arch;
106 const gpa = zld.gpa;
89 const cpu_arch = macho_file.base.options.target.cpu.arch;
90 const gpa = macho_file.base.allocator;
10791
10892 var eh_records = std.AutoArrayHashMap(u32, EhFrameRecord(true)).init(gpa);
10993 defer {
......@@ -115,7 +99,7 @@ pub fn write(zld: *Zld, unwind_info: *UnwindInfo) !void {
11599
116100 var eh_frame_offset: u32 = 0;
117101
118 for (zld.objects.items, 0..) |*object, object_id| {
102 for (macho_file.objects.items, 0..) |*object, object_id| {
119103 try eh_records.ensureUnusedCapacity(2 * @as(u32, @intCast(object.exec_atoms.items.len)));
120104
121105 var cies = std.AutoHashMap(u32, u32).init(gpa);
......@@ -124,7 +108,7 @@ pub fn write(zld: *Zld, unwind_info: *UnwindInfo) !void {
124108 var eh_it = object.getEhFrameRecordsIterator();
125109
126110 for (object.exec_atoms.items) |atom_index| {
127 var inner_syms_it = Atom.getInnerSymbolsIterator(zld, atom_index);
111 var inner_syms_it = Atom.getInnerSymbolsIterator(macho_file, atom_index);
128112 while (inner_syms_it.next()) |target| {
129113 const fde_record_offset = object.eh_frame_records_lookup.get(target) orelse continue;
130114 if (object.eh_frame_relocs_lookup.get(fde_record_offset).?.dead) continue;
......@@ -137,17 +121,17 @@ pub fn write(zld: *Zld, unwind_info: *UnwindInfo) !void {
137121 if (!is_dwarf) continue;
138122
139123 eh_it.seekTo(fde_record_offset);
140 const source_fde_record = (try eh_it.next()).?;
124 const source_fde_record = (eh_it.next() catch continue).?; // We already handled this error
141125
142 const cie_ptr = source_fde_record.getCiePointerSource(@intCast(object_id), zld, fde_record_offset);
126 const cie_ptr = source_fde_record.getCiePointerSource(@intCast(object_id), macho_file, fde_record_offset);
143127 const cie_offset = fde_record_offset + 4 - cie_ptr;
144128
145129 const gop = try cies.getOrPut(cie_offset);
146130 if (!gop.found_existing) {
147131 eh_it.seekTo(cie_offset);
148 const source_cie_record = (try eh_it.next()).?;
132 const source_cie_record = (eh_it.next() catch continue).?; // We already handled this error
149133 var cie_record = try source_cie_record.toOwned(gpa);
150 try cie_record.relocate(zld, @as(u32, @intCast(object_id)), .{
134 try cie_record.relocate(macho_file, @as(u32, @intCast(object_id)), .{
151135 .source_offset = cie_offset,
152136 .out_offset = eh_frame_offset,
153137 .sect_addr = sect.addr,
......@@ -158,7 +142,7 @@ pub fn write(zld: *Zld, unwind_info: *UnwindInfo) !void {
158142 }
159143
160144 var fde_record = try source_fde_record.toOwned(gpa);
161 try fde_record.relocate(zld, @as(u32, @intCast(object_id)), .{
145 try fde_record.relocate(macho_file, @as(u32, @intCast(object_id)), .{
162146 .source_offset = fde_record_offset,
163147 .out_offset = eh_frame_offset,
164148 .sect_addr = sect.addr,
......@@ -169,7 +153,7 @@ pub fn write(zld: *Zld, unwind_info: *UnwindInfo) !void {
169153 .aarch64 => {}, // relocs take care of LSDA pointers
170154 .x86_64 => {
171155 // We need to relocate target symbol address ourselves.
172 const atom_sym = zld.getSymbol(target);
156 const atom_sym = macho_file.getSymbol(target);
173157 try fde_record.setTargetSymbolAddress(atom_sym.n_value, .{
174158 .base_addr = sect.addr,
175159 .base_offset = eh_frame_offset,
......@@ -180,17 +164,17 @@ pub fn write(zld: *Zld, unwind_info: *UnwindInfo) !void {
180164 eh_frame_offset + 4 - fde_record.getCiePointer(),
181165 ).?;
182166 const eh_frame_sect = object.getSourceSection(object.eh_frame_sect_id.?);
183 const source_lsda_ptr = try fde_record.getLsdaPointer(cie_record, .{
167 const source_lsda_ptr = fde_record.getLsdaPointer(cie_record, .{
184168 .base_addr = eh_frame_sect.addr,
185169 .base_offset = fde_record_offset,
186 });
170 }) catch continue; // We already handled this error
187171 if (source_lsda_ptr) |ptr| {
188172 const sym_index = object.getSymbolByAddress(ptr, null);
189173 const sym = object.symtab[sym_index];
190 try fde_record.setLsdaPointer(cie_record, sym.n_value, .{
174 fde_record.setLsdaPointer(cie_record, sym.n_value, .{
191175 .base_addr = sect.addr,
192176 .base_offset = eh_frame_offset,
193 });
177 }) catch continue; // We already handled this error
194178 }
195179 },
196180 else => unreachable,
......@@ -207,10 +191,10 @@ pub fn write(zld: *Zld, unwind_info: *UnwindInfo) !void {
207191 const cie_record = eh_records.get(
208192 eh_frame_offset + 4 - fde_record.getCiePointer(),
209193 ).?;
210 const lsda_ptr = try fde_record.getLsdaPointer(cie_record, .{
194 const lsda_ptr = fde_record.getLsdaPointer(cie_record, .{
211195 .base_addr = sect.addr,
212196 .base_offset = eh_frame_offset,
213 });
197 }) catch continue; // We already handled this error
214198 if (lsda_ptr) |ptr| {
215199 record.lsda = ptr - seg.vmaddr;
216200 }
......@@ -229,7 +213,7 @@ pub fn write(zld: *Zld, unwind_info: *UnwindInfo) !void {
229213 try buffer.appendSlice(record.data);
230214 }
231215
232 try zld.file.pwriteAll(buffer.items, sect.offset);
216 try macho_file.base.file.?.pwriteAll(buffer.items, sect.offset);
233217}
234218const EhFrameRecordTag = enum { cie, fde };
235219
......@@ -261,12 +245,12 @@ pub fn EhFrameRecord(comptime is_mutable: bool) type {
261245
262246 pub fn scanRelocs(
263247 rec: Record,
264 zld: *Zld,
248 macho_file: *MachO,
265249 object_id: u32,
266250 source_offset: u32,
267251 ) !void {
268 if (rec.getPersonalityPointerReloc(zld, object_id, source_offset)) |target| {
269 try Atom.addGotEntry(zld, target);
252 if (rec.getPersonalityPointerReloc(macho_file, object_id, source_offset)) |target| {
253 try macho_file.addGotEntry(target);
270254 }
271255 }
272256
......@@ -290,12 +274,12 @@ pub fn EhFrameRecord(comptime is_mutable: bool) type {
290274
291275 pub fn getPersonalityPointerReloc(
292276 rec: Record,
293 zld: *Zld,
277 macho_file: *MachO,
294278 object_id: u32,
295279 source_offset: u32,
296280 ) ?SymbolWithLoc {
297 const cpu_arch = zld.options.target.cpu.arch;
298 const relocs = getRelocs(zld, object_id, source_offset);
281 const cpu_arch = macho_file.base.options.target.cpu.arch;
282 const relocs = getRelocs(macho_file, object_id, source_offset);
299283 for (relocs) |rel| {
300284 switch (cpu_arch) {
301285 .aarch64 => {
......@@ -317,7 +301,7 @@ pub fn EhFrameRecord(comptime is_mutable: bool) type {
317301 },
318302 else => unreachable,
319303 }
320 const target = Atom.parseRelocTarget(zld, .{
304 const target = Atom.parseRelocTarget(macho_file, .{
321305 .object_id = object_id,
322306 .rel = rel,
323307 .code = rec.data,
......@@ -328,18 +312,18 @@ pub fn EhFrameRecord(comptime is_mutable: bool) type {
328312 return null;
329313 }
330314
331 pub fn relocate(rec: *Record, zld: *Zld, object_id: u32, ctx: struct {
315 pub fn relocate(rec: *Record, macho_file: *MachO, object_id: u32, ctx: struct {
332316 source_offset: u32,
333317 out_offset: u32,
334318 sect_addr: u64,
335319 }) !void {
336320 comptime assert(is_mutable);
337321
338 const cpu_arch = zld.options.target.cpu.arch;
339 const relocs = getRelocs(zld, object_id, ctx.source_offset);
322 const cpu_arch = macho_file.base.options.target.cpu.arch;
323 const relocs = getRelocs(macho_file, object_id, ctx.source_offset);
340324
341325 for (relocs) |rel| {
342 const target = Atom.parseRelocTarget(zld, .{
326 const target = Atom.parseRelocTarget(macho_file, .{
343327 .object_id = object_id,
344328 .rel = rel,
345329 .code = rec.data,
......@@ -356,14 +340,14 @@ pub fn EhFrameRecord(comptime is_mutable: bool) type {
356340 // Address of the __eh_frame in the source object file
357341 },
358342 .ARM64_RELOC_POINTER_TO_GOT => {
359 const target_addr = try Atom.getRelocTargetAddress(zld, target, true, false);
343 const target_addr = macho_file.getGotEntryAddress(target).?;
360344 const result = math.cast(i32, @as(i64, @intCast(target_addr)) - @as(i64, @intCast(source_addr))) orelse
361345 return error.Overflow;
362346 mem.writeIntLittle(i32, rec.data[rel_offset..][0..4], result);
363347 },
364348 .ARM64_RELOC_UNSIGNED => {
365349 assert(rel.r_extern == 1);
366 const target_addr = try Atom.getRelocTargetAddress(zld, target, false, false);
350 const target_addr = Atom.getRelocTargetAddress(macho_file, target, false);
367351 const result = @as(i64, @intCast(target_addr)) - @as(i64, @intCast(source_addr));
368352 mem.writeIntLittle(i64, rec.data[rel_offset..][0..8], @as(i64, @intCast(result)));
369353 },
......@@ -374,7 +358,7 @@ pub fn EhFrameRecord(comptime is_mutable: bool) type {
374358 const rel_type = @as(macho.reloc_type_x86_64, @enumFromInt(rel.r_type));
375359 switch (rel_type) {
376360 .X86_64_RELOC_GOT => {
377 const target_addr = try Atom.getRelocTargetAddress(zld, target, true, false);
361 const target_addr = macho_file.getGotEntryAddress(target).?;
378362 const addend = mem.readIntLittle(i32, rec.data[rel_offset..][0..4]);
379363 const adjusted_target_addr = @as(u64, @intCast(@as(i64, @intCast(target_addr)) + addend));
380364 const disp = try Relocation.calcPcRelativeDisplacementX86(source_addr, adjusted_target_addr, 0);
......@@ -388,20 +372,20 @@ pub fn EhFrameRecord(comptime is_mutable: bool) type {
388372 }
389373 }
390374
391 pub fn getCiePointerSource(rec: Record, object_id: u32, zld: *Zld, offset: u32) u32 {
375 pub fn getCiePointerSource(rec: Record, object_id: u32, macho_file: *MachO, offset: u32) u32 {
392376 assert(rec.tag == .fde);
393 const cpu_arch = zld.options.target.cpu.arch;
377 const cpu_arch = macho_file.base.options.target.cpu.arch;
394378 const addend = mem.readIntLittle(u32, rec.data[0..4]);
395379 switch (cpu_arch) {
396380 .aarch64 => {
397 const relocs = getRelocs(zld, object_id, offset);
381 const relocs = getRelocs(macho_file, object_id, offset);
398382 const maybe_rel = for (relocs) |rel| {
399383 if (rel.r_address - @as(i32, @intCast(offset)) == 4 and
400384 @as(macho.reloc_type_arm64, @enumFromInt(rel.r_type)) == .ARM64_RELOC_SUBTRACTOR)
401385 break rel;
402386 } else null;
403387 const rel = maybe_rel orelse return addend;
404 const object = &zld.objects.items[object_id];
388 const object = &macho_file.objects.items[object_id];
405389 const target_addr = object.in_symtab.?[rel.r_symbolnum].n_value;
406390 const sect = object.getSourceSection(object.eh_frame_sect_id.?);
407391 return @intCast(sect.addr + offset - target_addr + addend);
......@@ -583,8 +567,8 @@ pub fn EhFrameRecord(comptime is_mutable: bool) type {
583567 };
584568}
585569
586pub fn getRelocs(zld: *Zld, object_id: u32, source_offset: u32) []const macho.relocation_info {
587 const object = &zld.objects.items[object_id];
570pub fn getRelocs(macho_file: *MachO, object_id: u32, source_offset: u32) []const macho.relocation_info {
571 const object = &macho_file.objects.items[object_id];
588572 assert(object.hasEhFrameRecords());
589573 const urel = object.eh_frame_relocs_lookup.get(source_offset) orelse
590574 return &[0]macho.relocation_info{};
......@@ -604,7 +588,7 @@ pub const Iterator = struct {
604588
605589 var size = try reader.readIntLittle(u32);
606590 if (size == 0xFFFFFFFF) {
607 log.err("MachO doesn't support 64bit DWARF CFI __eh_frame records", .{});
591 log.debug("MachO doesn't support 64bit DWARF CFI __eh_frame records", .{});
608592 return error.BadDwarfCfi;
609593 }
610594
......@@ -650,3 +634,18 @@ pub const EH_PE = struct {
650634 pub const indirect = 0x80;
651635 pub const omit = 0xFF;
652636};
637
638const std = @import("std");
639const assert = std.debug.assert;
640const macho = std.macho;
641const math = std.math;
642const mem = std.mem;
643const leb = std.leb;
644const log = std.log.scoped(.eh_frame);
645
646const Allocator = mem.Allocator;
647const Atom = @import("Atom.zig");
648const MachO = @import("../MachO.zig");
649const Relocation = @import("Relocation.zig");
650const SymbolWithLoc = MachO.SymbolWithLoc;
651const UnwindInfo = @import("UnwindInfo.zig");
src/link/MachO/fat.zig+32-30
......@@ -1,42 +1,44 @@
1const std = @import("std");
2const log = std.log.scoped(.archive);
3const macho = std.macho;
4const mem = std.mem;
5
6pub fn decodeArch(cputype: macho.cpu_type_t, comptime logError: bool) !std.Target.Cpu.Arch {
7 const cpu_arch: std.Target.Cpu.Arch = switch (cputype) {
8 macho.CPU_TYPE_ARM64 => .aarch64,
9 macho.CPU_TYPE_X86_64 => .x86_64,
10 else => {
11 if (logError) {
12 log.err("unsupported cpu architecture 0x{x}", .{cputype});
13 }
14 return error.UnsupportedCpuArchitecture;
15 },
16 };
17 return cpu_arch;
1pub fn isFatLibrary(file: std.fs.File) bool {
2 const reader = file.reader();
3 const hdr = reader.readStructBig(macho.fat_header) catch return false;
4 defer file.seekTo(0) catch {};
5 return hdr.magic == macho.FAT_MAGIC;
186}
197
20pub fn getLibraryOffset(reader: anytype, cpu_arch: std.Target.Cpu.Arch) !u64 {
8pub const Arch = struct {
9 tag: std.Target.Cpu.Arch,
10 offset: u64,
11};
12
13/// Caller owns the memory.
14pub fn parseArchs(gpa: Allocator, file: std.fs.File) ![]const Arch {
15 const reader = file.reader();
2116 const fat_header = try reader.readStructBig(macho.fat_header);
22 if (fat_header.magic != macho.FAT_MAGIC) return 0;
17 assert(fat_header.magic == macho.FAT_MAGIC);
18
19 var archs = try std.ArrayList(Arch).initCapacity(gpa, fat_header.nfat_arch);
20 defer archs.deinit();
2321
2422 var fat_arch_index: u32 = 0;
2523 while (fat_arch_index < fat_header.nfat_arch) : (fat_arch_index += 1) {
2624 const fat_arch = try reader.readStructBig(macho.fat_arch);
2725 // If we come across an architecture that we do not know how to handle, that's
2826 // fine because we can keep looking for one that might match.
29 const lib_arch = decodeArch(fat_arch.cputype, false) catch |err| switch (err) {
30 error.UnsupportedCpuArchitecture => continue,
27 const arch: std.Target.Cpu.Arch = switch (fat_arch.cputype) {
28 macho.CPU_TYPE_ARM64 => if (fat_arch.cpusubtype == macho.CPU_SUBTYPE_ARM_ALL) .aarch64 else continue,
29 macho.CPU_TYPE_X86_64 => if (fat_arch.cpusubtype == macho.CPU_SUBTYPE_X86_64_ALL) .x86_64 else continue,
30 else => continue,
3131 };
32 if (lib_arch == cpu_arch) {
33 // We have found a matching architecture!
34 return fat_arch.offset;
35 }
36 } else {
37 log.err("Could not find matching cpu architecture in fat library: expected {s}", .{
38 @tagName(cpu_arch),
39 });
40 return error.MismatchedCpuArchitecture;
32
33 archs.appendAssumeCapacity(.{ .tag = arch, .offset = fat_arch.offset });
4134 }
35
36 return archs.toOwnedSlice();
4237}
38
39const std = @import("std");
40const assert = std.debug.assert;
41const log = std.log.scoped(.archive);
42const macho = std.macho;
43const mem = std.mem;
44const Allocator = mem.Allocator;
src/link/MachO/hasher.zig+9-9
......@@ -1,12 +1,3 @@
1const std = @import("std");
2const assert = std.debug.assert;
3const fs = std.fs;
4const mem = std.mem;
5
6const Allocator = mem.Allocator;
7const ThreadPool = std.Thread.Pool;
8const WaitGroup = std.Thread.WaitGroup;
9
101pub fn ParallelHasher(comptime Hasher: type) type {
112 const hash_size = Hasher.digest_length;
123
......@@ -69,3 +60,12 @@ pub fn ParallelHasher(comptime Hasher: type) type {
6960 const Self = @This();
7061 };
7162}
63
64const std = @import("std");
65const assert = std.debug.assert;
66const fs = std.fs;
67const mem = std.mem;
68
69const Allocator = mem.Allocator;
70const ThreadPool = std.Thread.Pool;
71const WaitGroup = std.Thread.WaitGroup;
src/link/MachO/load_commands.zig+194-48
......@@ -1,13 +1,3 @@
1const std = @import("std");
2const assert = std.debug.assert;
3const link = @import("../../link.zig");
4const log = std.log.scoped(.link);
5const macho = std.macho;
6const mem = std.mem;
7
8const Allocator = mem.Allocator;
9const Dylib = @import("Dylib.zig");
10
111/// Default implicit entrypoint symbol name.
122pub const default_entry_point: []const u8 = "_main";
133
......@@ -86,8 +76,14 @@ fn calcLCsSize(gpa: Allocator, options: *const link.Options, ctx: CalcLCsSizeCtx
8676 }
8777 // LC_SOURCE_VERSION
8878 sizeofcmds += @sizeOf(macho.source_version_command);
89 // LC_BUILD_VERSION
90 sizeofcmds += @sizeOf(macho.build_version_command) + @sizeOf(macho.build_tool_version);
79 // LC_BUILD_VERSION or LC_VERSION_MIN_
80 if (Platform.fromTarget(options.target).isBuildVersionCompatible()) {
81 // LC_BUILD_VERSION
82 sizeofcmds += @sizeOf(macho.build_version_command) + @sizeOf(macho.build_tool_version);
83 } else {
84 // LC_VERSION_MIN_
85 sizeofcmds += @sizeOf(macho.version_min_command);
86 }
9187 // LC_UUID
9288 sizeofcmds += @sizeOf(macho.uuid_command);
9389 // LC_LOAD_DYLIB
......@@ -101,17 +97,8 @@ fn calcLCsSize(gpa: Allocator, options: *const link.Options, ctx: CalcLCsSizeCtx
10197 );
10298 }
10399 // LC_CODE_SIGNATURE
104 {
105 const target = options.target;
106 const requires_codesig = blk: {
107 if (options.entitlements) |_| break :blk true;
108 if (target.cpu.arch == .aarch64 and (target.os.tag == .macos or target.abi == .simulator))
109 break :blk true;
110 break :blk false;
111 };
112 if (requires_codesig) {
113 sizeofcmds += @sizeOf(macho.linkedit_data_command);
114 }
100 if (MachO.requiresCodeSignature(options)) {
101 sizeofcmds += @sizeOf(macho.linkedit_data_command);
115102 }
116103
117104 return @as(u32, @intCast(sizeofcmds));
......@@ -271,33 +258,28 @@ pub fn writeRpathLCs(gpa: Allocator, options: *const link.Options, lc_writer: an
271258 }
272259}
273260
274pub fn writeBuildVersionLC(options: *const link.Options, lc_writer: anytype) !void {
275 const cmdsize = @sizeOf(macho.build_version_command) + @sizeOf(macho.build_tool_version);
276 const platform_version = blk: {
277 const ver = options.target.os.version_range.semver.min;
278 const platform_version = @as(u32, @intCast(ver.major << 16 | ver.minor << 8));
279 break :blk platform_version;
261pub fn writeVersionMinLC(platform: Platform, sdk_version: ?std.SemanticVersion, lc_writer: anytype) !void {
262 const cmd: macho.LC = switch (platform.os_tag) {
263 .macos => .VERSION_MIN_MACOSX,
264 .ios => .VERSION_MIN_IPHONEOS,
265 .tvos => .VERSION_MIN_TVOS,
266 .watchos => .VERSION_MIN_WATCHOS,
267 else => unreachable,
280268 };
281 const sdk_version: ?std.SemanticVersion = options.darwin_sdk_version orelse blk: {
282 if (options.sysroot) |path| break :blk inferSdkVersionFromSdkPath(path);
283 break :blk null;
284 };
285 const sdk_version_value: u32 = if (sdk_version) |ver|
286 @intCast(ver.major << 16 | ver.minor << 8)
287 else
288 platform_version;
289 const is_simulator_abi = options.target.abi == .simulator;
269 try lc_writer.writeAll(mem.asBytes(&macho.version_min_command{
270 .cmd = cmd,
271 .version = platform.toAppleVersion(),
272 .sdk = if (sdk_version) |ver| semanticVersionToAppleVersion(ver) else platform.toAppleVersion(),
273 }));
274}
275
276pub fn writeBuildVersionLC(platform: Platform, sdk_version: ?std.SemanticVersion, lc_writer: anytype) !void {
277 const cmdsize = @sizeOf(macho.build_version_command) + @sizeOf(macho.build_tool_version);
290278 try lc_writer.writeStruct(macho.build_version_command{
291279 .cmdsize = cmdsize,
292 .platform = switch (options.target.os.tag) {
293 .macos => .MACOS,
294 .ios => if (is_simulator_abi) macho.PLATFORM.IOSSIMULATOR else macho.PLATFORM.IOS,
295 .watchos => if (is_simulator_abi) macho.PLATFORM.WATCHOSSIMULATOR else macho.PLATFORM.WATCHOS,
296 .tvos => if (is_simulator_abi) macho.PLATFORM.TVOSSIMULATOR else macho.PLATFORM.TVOS,
297 else => unreachable,
298 },
299 .minos = platform_version,
300 .sdk = sdk_version_value,
280 .platform = platform.toApplePlatform(),
281 .minos = platform.toAppleVersion(),
282 .sdk = if (sdk_version) |ver| semanticVersionToAppleVersion(ver) else platform.toAppleVersion(),
301283 .ntools = 1,
302284 });
303285 try lc_writer.writeAll(mem.asBytes(&macho.build_tool_version{
......@@ -320,7 +302,160 @@ pub fn writeLoadDylibLCs(dylibs: []const Dylib, referenced: []u16, lc_writer: an
320302 }
321303}
322304
323fn inferSdkVersionFromSdkPath(path: []const u8) ?std.SemanticVersion {
305pub const Platform = struct {
306 os_tag: std.Target.Os.Tag,
307 abi: std.Target.Abi,
308 version: std.SemanticVersion,
309
310 /// Using Apple's ld64 as our blueprint, `min_version` as well as `sdk_version` are set to
311 /// the extracted minimum platform version.
312 pub fn fromLoadCommand(lc: macho.LoadCommandIterator.LoadCommand) Platform {
313 switch (lc.cmd()) {
314 .BUILD_VERSION => {
315 const cmd = lc.cast(macho.build_version_command).?;
316 return .{
317 .os_tag = switch (cmd.platform) {
318 .MACOS => .macos,
319 .IOS, .IOSSIMULATOR => .ios,
320 .TVOS, .TVOSSIMULATOR => .tvos,
321 .WATCHOS, .WATCHOSSIMULATOR => .watchos,
322 else => @panic("TODO"),
323 },
324 .abi = switch (cmd.platform) {
325 .IOSSIMULATOR,
326 .TVOSSIMULATOR,
327 .WATCHOSSIMULATOR,
328 => .simulator,
329 else => .none,
330 },
331 .version = appleVersionToSemanticVersion(cmd.minos),
332 };
333 },
334 .VERSION_MIN_MACOSX,
335 .VERSION_MIN_IPHONEOS,
336 .VERSION_MIN_TVOS,
337 .VERSION_MIN_WATCHOS,
338 => {
339 const cmd = lc.cast(macho.version_min_command).?;
340 return .{
341 .os_tag = switch (lc.cmd()) {
342 .VERSION_MIN_MACOSX => .macos,
343 .VERSION_MIN_IPHONEOS => .ios,
344 .VERSION_MIN_TVOS => .tvos,
345 .VERSION_MIN_WATCHOS => .watchos,
346 else => unreachable,
347 },
348 .abi = .none,
349 .version = appleVersionToSemanticVersion(cmd.version),
350 };
351 },
352 else => unreachable,
353 }
354 }
355
356 pub fn fromTarget(target: std.Target) Platform {
357 return .{
358 .os_tag = target.os.tag,
359 .abi = target.abi,
360 .version = target.os.version_range.semver.min,
361 };
362 }
363
364 pub fn toAppleVersion(plat: Platform) u32 {
365 return semanticVersionToAppleVersion(plat.version);
366 }
367
368 pub fn toApplePlatform(plat: Platform) macho.PLATFORM {
369 return switch (plat.os_tag) {
370 .macos => .MACOS,
371 .ios => if (plat.abi == .simulator) .IOSSIMULATOR else .IOS,
372 .tvos => if (plat.abi == .simulator) .TVOSSIMULATOR else .TVOS,
373 .watchos => if (plat.abi == .simulator) .WATCHOSSIMULATOR else .WATCHOS,
374 else => unreachable,
375 };
376 }
377
378 pub fn isBuildVersionCompatible(plat: Platform) bool {
379 inline for (supported_platforms) |sup_plat| {
380 if (sup_plat[0] == plat.os_tag and sup_plat[1] == plat.abi) {
381 return sup_plat[2] <= plat.toAppleVersion();
382 }
383 }
384 return false;
385 }
386
387 pub fn fmtTarget(plat: Platform, cpu_arch: std.Target.Cpu.Arch) std.fmt.Formatter(formatTarget) {
388 return .{ .data = .{ .platform = plat, .cpu_arch = cpu_arch } };
389 }
390
391 const FmtCtx = struct {
392 platform: Platform,
393 cpu_arch: std.Target.Cpu.Arch,
394 };
395
396 pub fn formatTarget(
397 ctx: FmtCtx,
398 comptime unused_fmt_string: []const u8,
399 options: std.fmt.FormatOptions,
400 writer: anytype,
401 ) !void {
402 _ = unused_fmt_string;
403 _ = options;
404 try writer.print("{s}-{s}", .{ @tagName(ctx.cpu_arch), @tagName(ctx.platform.os_tag) });
405 if (ctx.platform.abi != .none) {
406 try writer.print("-{s}", .{@tagName(ctx.platform.abi)});
407 }
408 }
409
410 /// Caller owns the memory.
411 pub fn allocPrintTarget(plat: Platform, gpa: Allocator, cpu_arch: std.Target.Cpu.Arch) error{OutOfMemory}![]u8 {
412 var buffer = std.ArrayList(u8).init(gpa);
413 defer buffer.deinit();
414 try buffer.writer().print("{}", .{plat.fmtTarget(cpu_arch)});
415 return buffer.toOwnedSlice();
416 }
417
418 pub fn eqlTarget(plat: Platform, other: Platform) bool {
419 return plat.os_tag == other.os_tag and plat.abi == other.abi;
420 }
421};
422
423const SupportedPlatforms = struct {
424 std.Target.Os.Tag,
425 std.Target.Abi,
426 u32, // Min platform version for which to emit LC_BUILD_VERSION
427 u32, // Min supported platform version
428};
429
430// Source: https://github.com/apple-oss-distributions/ld64/blob/59a99ab60399c5e6c49e6945a9e1049c42b71135/src/ld/PlatformSupport.cpp#L52
431// zig fmt: off
432const supported_platforms = [_]SupportedPlatforms{
433 .{ .macos, .none, 0xA0E00, 0xA0800 },
434 .{ .ios, .none, 0xC0000, 0x70000 },
435 .{ .tvos, .none, 0xC0000, 0x70000 },
436 .{ .watchos, .none, 0x50000, 0x20000 },
437 .{ .ios, .simulator, 0xD0000, 0x80000 },
438 .{ .tvos, .simulator, 0xD0000, 0x80000 },
439 .{ .watchos, .simulator, 0x60000, 0x20000 },
440};
441// zig fmt: on
442
443inline fn semanticVersionToAppleVersion(version: std.SemanticVersion) u32 {
444 const major = version.major;
445 const minor = version.minor;
446 const patch = version.patch;
447 return (@as(u32, @intCast(major)) << 16) | (@as(u32, @intCast(minor)) << 8) | @as(u32, @intCast(patch));
448}
449
450pub inline fn appleVersionToSemanticVersion(version: u32) std.SemanticVersion {
451 return .{
452 .major = @as(u16, @truncate(version >> 16)),
453 .minor = @as(u8, @truncate(version >> 8)),
454 .patch = @as(u8, @truncate(version)),
455 };
456}
457
458pub fn inferSdkVersionFromSdkPath(path: []const u8) ?std.SemanticVersion {
324459 const stem = std.fs.path.stem(path);
325460 const start = for (stem, 0..) |c, i| {
326461 if (std.ascii.isDigit(c)) break i;
......@@ -374,3 +509,14 @@ test "parseSdkVersion" {
374509
375510 try expect(parseSdkVersion("11") == null);
376511}
512
513const std = @import("std");
514const assert = std.debug.assert;
515const link = @import("../../link.zig");
516const log = std.log.scoped(.link);
517const macho = std.macho;
518const mem = std.mem;
519
520const Allocator = mem.Allocator;
521const Dylib = @import("Dylib.zig");
522const MachO = @import("../MachO.zig");
src/link/MachO/stubs.zig+17-9
......@@ -1,9 +1,4 @@
1const std = @import("std");
2const aarch64 = @import("../../arch/aarch64/bits.zig");
3
4const Relocation = @import("Relocation.zig");
5
6pub inline fn calcStubHelperPreambleSize(cpu_arch: std.Target.Cpu.Arch) u5 {
1pub inline fn stubHelperPreambleSize(cpu_arch: std.Target.Cpu.Arch) u8 {
72 return switch (cpu_arch) {
83 .x86_64 => 15,
94 .aarch64 => 6 * @sizeOf(u32),
......@@ -11,7 +6,7 @@ pub inline fn calcStubHelperPreambleSize(cpu_arch: std.Target.Cpu.Arch) u5 {
116 };
127}
138
14pub inline fn calcStubHelperEntrySize(cpu_arch: std.Target.Cpu.Arch) u4 {
9pub inline fn stubHelperSize(cpu_arch: std.Target.Cpu.Arch) u8 {
1510 return switch (cpu_arch) {
1611 .x86_64 => 10,
1712 .aarch64 => 3 * @sizeOf(u32),
......@@ -19,7 +14,7 @@ pub inline fn calcStubHelperEntrySize(cpu_arch: std.Target.Cpu.Arch) u4 {
1914 };
2015}
2116
22pub inline fn calcStubEntrySize(cpu_arch: std.Target.Cpu.Arch) u4 {
17pub inline fn stubSize(cpu_arch: std.Target.Cpu.Arch) u8 {
2318 return switch (cpu_arch) {
2419 .x86_64 => 6,
2520 .aarch64 => 3 * @sizeOf(u32),
......@@ -27,7 +22,15 @@ pub inline fn calcStubEntrySize(cpu_arch: std.Target.Cpu.Arch) u4 {
2722 };
2823}
2924
30pub inline fn calcStubOffsetInStubHelper(cpu_arch: std.Target.Cpu.Arch) u4 {
25pub inline fn stubAlignment(cpu_arch: std.Target.Cpu.Arch) u8 {
26 return switch (cpu_arch) {
27 .x86_64 => 1,
28 .aarch64 => 4,
29 else => unreachable, // unhandled architecture type
30 };
31}
32
33pub inline fn stubOffsetInStubHelper(cpu_arch: std.Target.Cpu.Arch) u8 {
3134 return switch (cpu_arch) {
3235 .x86_64 => 1,
3336 .aarch64 => 2 * @sizeOf(u32),
......@@ -159,3 +162,8 @@ pub fn writeStubCode(args: struct {
159162 else => unreachable,
160163 }
161164}
165
166const std = @import("std");
167const aarch64 = @import("../../arch/aarch64/bits.zig");
168
169const Relocation = @import("Relocation.zig");
src/link/MachO/thunks.zig+143-147
......@@ -5,24 +5,6 @@
55//! The algorithm works pessimistically and assumes that any reference to an Atom in
66//! another output section is out of range.
77
8const std = @import("std");
9const assert = std.debug.assert;
10const log = std.log.scoped(.thunks);
11const macho = std.macho;
12const math = std.math;
13const mem = std.mem;
14
15const aarch64 = @import("../../arch/aarch64/bits.zig");
16
17const Allocator = mem.Allocator;
18const Atom = @import("ZldAtom.zig");
19const AtomIndex = @import("zld.zig").AtomIndex;
20const Relocation = @import("Relocation.zig");
21const SymbolWithLoc = @import("zld.zig").SymbolWithLoc;
22const Zld = @import("zld.zig").Zld;
23
24pub const ThunkIndex = u32;
25
268/// Branch instruction has 26 bits immediate but 4 byte aligned.
279const jump_bits = @bitSizeOf(i28);
2810
......@@ -35,21 +17,35 @@ const max_distance = (1 << (jump_bits - 1));
3517const max_allowed_distance = max_distance - 0x500_000;
3618
3719pub const Thunk = struct {
38 start_index: AtomIndex,
20 start_index: Atom.Index,
3921 len: u32,
4022
41 lookup: std.AutoArrayHashMapUnmanaged(SymbolWithLoc, AtomIndex) = .{},
23 targets: std.MultiArrayList(Target) = .{},
24 lookup: std.AutoHashMapUnmanaged(Target, u32) = .{},
25
26 pub const Tag = enum {
27 stub,
28 atom,
29 };
30
31 pub const Target = struct {
32 tag: Tag,
33 target: SymbolWithLoc,
34 };
35
36 pub const Index = u32;
4237
4338 pub fn deinit(self: *Thunk, gpa: Allocator) void {
39 self.targets.deinit(gpa);
4440 self.lookup.deinit(gpa);
4541 }
4642
47 pub fn getStartAtomIndex(self: Thunk) AtomIndex {
43 pub fn getStartAtomIndex(self: Thunk) Atom.Index {
4844 assert(self.len != 0);
4945 return self.start_index;
5046 }
5147
52 pub fn getEndAtomIndex(self: Thunk) AtomIndex {
48 pub fn getEndAtomIndex(self: Thunk) Atom.Index {
5349 assert(self.len != 0);
5450 return self.start_index + self.len - 1;
5551 }
......@@ -62,19 +58,18 @@ pub const Thunk = struct {
6258 return @alignOf(u32);
6359 }
6460
65 pub fn getTrampolineForSymbol(self: Thunk, zld: *Zld, target: SymbolWithLoc) ?SymbolWithLoc {
66 const atom_index = self.lookup.get(target) orelse return null;
67 const atom = zld.getAtom(atom_index);
68 return atom.getSymbolWithLoc();
61 pub fn getTrampoline(self: Thunk, macho_file: *MachO, tag: Tag, target: SymbolWithLoc) ?SymbolWithLoc {
62 const atom_index = self.lookup.get(.{ .tag = tag, .target = target }) orelse return null;
63 return macho_file.getAtom(atom_index).getSymbolWithLoc();
6964 }
7065};
7166
72pub fn createThunks(zld: *Zld, sect_id: u8) !void {
73 const header = &zld.sections.items(.header)[sect_id];
67pub fn createThunks(macho_file: *MachO, sect_id: u8) !void {
68 const header = &macho_file.sections.items(.header)[sect_id];
7469 if (header.size == 0) return;
7570
76 const gpa = zld.gpa;
77 const first_atom_index = zld.sections.items(.first_atom_index)[sect_id];
71 const gpa = macho_file.base.allocator;
72 const first_atom_index = macho_file.sections.items(.first_atom_index)[sect_id].?;
7873
7974 header.size = 0;
8075 header.@"align" = 0;
......@@ -84,8 +79,8 @@ pub fn createThunks(zld: *Zld, sect_id: u8) !void {
8479 {
8580 var atom_index = first_atom_index;
8681 while (true) {
87 const atom = zld.getAtom(atom_index);
88 const sym = zld.getSymbolPtr(atom.getSymbolWithLoc());
82 const atom = macho_file.getAtom(atom_index);
83 const sym = macho_file.getSymbolPtr(atom.getSymbolWithLoc());
8984 sym.n_value = 0;
9085 atom_count += 1;
9186
......@@ -95,7 +90,7 @@ pub fn createThunks(zld: *Zld, sect_id: u8) !void {
9590 }
9691 }
9792
98 var allocated = std.AutoHashMap(AtomIndex, void).init(gpa);
93 var allocated = std.AutoHashMap(Atom.Index, void).init(gpa);
9994 defer allocated.deinit();
10095 try allocated.ensureTotalCapacity(atom_count);
10196
......@@ -104,24 +99,24 @@ pub fn createThunks(zld: *Zld, sect_id: u8) !void {
10499 var offset: u64 = 0;
105100
106101 while (true) {
107 const group_start_atom = zld.getAtom(group_start);
102 const group_start_atom = macho_file.getAtom(group_start);
108103 log.debug("GROUP START at {d}", .{group_start});
109104
110105 while (true) {
111 const atom = zld.getAtom(group_end);
106 const atom = macho_file.getAtom(group_end);
112107 offset = mem.alignForward(u64, offset, try math.powi(u32, 2, atom.alignment));
113108
114 const sym = zld.getSymbolPtr(atom.getSymbolWithLoc());
109 const sym = macho_file.getSymbolPtr(atom.getSymbolWithLoc());
115110 sym.n_value = offset;
116111 offset += atom.size;
117112
118 zld.logAtom(group_end, log);
113 macho_file.logAtom(group_end, log);
119114
120115 header.@"align" = @max(header.@"align", atom.alignment);
121116
122117 allocated.putAssumeCapacityNoClobber(group_end, {});
123118
124 const group_start_sym = zld.getSymbol(group_start_atom.getSymbolWithLoc());
119 const group_start_sym = macho_file.getSymbol(group_start_atom.getSymbolWithLoc());
125120 if (offset - group_start_sym.n_value >= max_allowed_distance) break;
126121
127122 if (atom.next_index) |next_index| {
......@@ -131,15 +126,15 @@ pub fn createThunks(zld: *Zld, sect_id: u8) !void {
131126 log.debug("GROUP END at {d}", .{group_end});
132127
133128 // Insert thunk at group_end
134 const thunk_index = @as(u32, @intCast(zld.thunks.items.len));
135 try zld.thunks.append(gpa, .{ .start_index = undefined, .len = 0 });
129 const thunk_index = @as(u32, @intCast(macho_file.thunks.items.len));
130 try macho_file.thunks.append(gpa, .{ .start_index = undefined, .len = 0 });
136131
137132 // Scan relocs in the group and create trampolines for any unreachable callsite.
138133 var atom_index = group_start;
139134 while (true) {
140 const atom = zld.getAtom(atom_index);
135 const atom = macho_file.getAtom(atom_index);
141136 try scanRelocs(
142 zld,
137 macho_file,
143138 atom_index,
144139 allocated,
145140 thunk_index,
......@@ -154,19 +149,19 @@ pub fn createThunks(zld: *Zld, sect_id: u8) !void {
154149 }
155150
156151 offset = mem.alignForward(u64, offset, Thunk.getAlignment());
157 allocateThunk(zld, thunk_index, offset, header);
158 offset += zld.thunks.items[thunk_index].getSize();
152 allocateThunk(macho_file, thunk_index, offset, header);
153 offset += macho_file.thunks.items[thunk_index].getSize();
159154
160 const thunk = zld.thunks.items[thunk_index];
155 const thunk = macho_file.thunks.items[thunk_index];
161156 if (thunk.len == 0) {
162 const group_end_atom = zld.getAtom(group_end);
157 const group_end_atom = macho_file.getAtom(group_end);
163158 if (group_end_atom.next_index) |next_index| {
164159 group_start = next_index;
165160 group_end = next_index;
166161 } else break;
167162 } else {
168163 const thunk_end_atom_index = thunk.getEndAtomIndex();
169 const thunk_end_atom = zld.getAtom(thunk_end_atom_index);
164 const thunk_end_atom = macho_file.getAtom(thunk_end_atom_index);
170165 if (thunk_end_atom.next_index) |next_index| {
171166 group_start = next_index;
172167 group_end = next_index;
......@@ -178,12 +173,12 @@ pub fn createThunks(zld: *Zld, sect_id: u8) !void {
178173}
179174
180175fn allocateThunk(
181 zld: *Zld,
182 thunk_index: ThunkIndex,
176 macho_file: *MachO,
177 thunk_index: Thunk.Index,
183178 base_offset: u64,
184179 header: *macho.section_64,
185180) void {
186 const thunk = zld.thunks.items[thunk_index];
181 const thunk = macho_file.thunks.items[thunk_index];
187182 if (thunk.len == 0) return;
188183
189184 const first_atom_index = thunk.getStartAtomIndex();
......@@ -192,14 +187,14 @@ fn allocateThunk(
192187 var atom_index = first_atom_index;
193188 var offset = base_offset;
194189 while (true) {
195 const atom = zld.getAtom(atom_index);
190 const atom = macho_file.getAtom(atom_index);
196191 offset = mem.alignForward(u64, offset, Thunk.getAlignment());
197192
198 const sym = zld.getSymbolPtr(atom.getSymbolWithLoc());
193 const sym = macho_file.getSymbolPtr(atom.getSymbolWithLoc());
199194 sym.n_value = offset;
200195 offset += atom.size;
201196
202 zld.logAtom(atom_index, log);
197 macho_file.logAtom(atom_index, log);
203198
204199 header.@"align" = @max(header.@"align", atom.alignment);
205200
......@@ -212,80 +207,83 @@ fn allocateThunk(
212207}
213208
214209fn scanRelocs(
215 zld: *Zld,
216 atom_index: AtomIndex,
217 allocated: std.AutoHashMap(AtomIndex, void),
218 thunk_index: ThunkIndex,
219 group_end: AtomIndex,
210 macho_file: *MachO,
211 atom_index: Atom.Index,
212 allocated: std.AutoHashMap(Atom.Index, void),
213 thunk_index: Thunk.Index,
214 group_end: Atom.Index,
220215) !void {
221 const atom = zld.getAtom(atom_index);
222 const object = zld.objects.items[atom.getFile().?];
216 const atom = macho_file.getAtom(atom_index);
217 const object = macho_file.objects.items[atom.getFile().?];
223218
224219 const base_offset = if (object.getSourceSymbol(atom.sym_index)) |source_sym| blk: {
225220 const source_sect = object.getSourceSection(source_sym.n_sect - 1);
226221 break :blk @as(i32, @intCast(source_sym.n_value - source_sect.addr));
227222 } else 0;
228223
229 const code = Atom.getAtomCode(zld, atom_index);
230 const relocs = Atom.getAtomRelocs(zld, atom_index);
231 const ctx = Atom.getRelocContext(zld, atom_index);
224 const code = Atom.getAtomCode(macho_file, atom_index);
225 const relocs = Atom.getAtomRelocs(macho_file, atom_index);
226 const ctx = Atom.getRelocContext(macho_file, atom_index);
232227
233228 for (relocs) |rel| {
234229 if (!relocNeedsThunk(rel)) continue;
235230
236 const target = Atom.parseRelocTarget(zld, .{
231 const target = Atom.parseRelocTarget(macho_file, .{
237232 .object_id = atom.getFile().?,
238233 .rel = rel,
239234 .code = code,
240235 .base_offset = ctx.base_offset,
241236 .base_addr = ctx.base_addr,
242237 });
243 if (isReachable(zld, atom_index, rel, base_offset, target, allocated)) continue;
238 if (isReachable(macho_file, atom_index, rel, base_offset, target, allocated)) continue;
244239
245240 log.debug("{x}: source = {s}@{x}, target = {s}@{x} unreachable", .{
246241 rel.r_address - base_offset,
247 zld.getSymbolName(atom.getSymbolWithLoc()),
248 zld.getSymbol(atom.getSymbolWithLoc()).n_value,
249 zld.getSymbolName(target),
250 zld.getSymbol(target).n_value,
242 macho_file.getSymbolName(atom.getSymbolWithLoc()),
243 macho_file.getSymbol(atom.getSymbolWithLoc()).n_value,
244 macho_file.getSymbolName(target),
245 macho_file.getSymbol(target).n_value,
251246 });
252247
253 const gpa = zld.gpa;
254 const target_sym = zld.getSymbol(target);
248 const gpa = macho_file.base.allocator;
249 const target_sym = macho_file.getSymbol(target);
250 const thunk = &macho_file.thunks.items[thunk_index];
255251
256 const actual_target: SymbolWithLoc = if (target_sym.undf()) blk: {
257 const stub_atom_index = zld.getStubsAtomIndexForSymbol(target).?;
258 break :blk .{ .sym_index = zld.getAtom(stub_atom_index).sym_index };
259 } else target;
260
261 const thunk = &zld.thunks.items[thunk_index];
262 const gop = try thunk.lookup.getOrPut(gpa, actual_target);
252 const tag: Thunk.Tag = if (target_sym.undf()) .stub else .atom;
253 const thunk_target: Thunk.Target = .{ .tag = tag, .target = target };
254 const gop = try thunk.lookup.getOrPut(gpa, thunk_target);
263255 if (!gop.found_existing) {
264 const thunk_atom_index = try createThunkAtom(zld);
265 gop.value_ptr.* = thunk_atom_index;
256 gop.value_ptr.* = try pushThunkAtom(macho_file, thunk, group_end);
257 try thunk.targets.append(gpa, thunk_target);
258 }
266259
267 const thunk_atom = zld.getAtomPtr(thunk_atom_index);
268 const end_atom_index = if (thunk.len == 0) group_end else thunk.getEndAtomIndex();
269 const end_atom = zld.getAtomPtr(end_atom_index);
260 try macho_file.thunk_table.put(gpa, atom_index, thunk_index);
261 }
262}
270263
271 if (end_atom.next_index) |first_after_index| {
272 const first_after_atom = zld.getAtomPtr(first_after_index);
273 first_after_atom.prev_index = thunk_atom_index;
274 thunk_atom.next_index = first_after_index;
275 }
264fn pushThunkAtom(macho_file: *MachO, thunk: *Thunk, group_end: Atom.Index) !Atom.Index {
265 const thunk_atom_index = try createThunkAtom(macho_file);
276266
277 end_atom.next_index = thunk_atom_index;
278 thunk_atom.prev_index = end_atom_index;
267 const thunk_atom = macho_file.getAtomPtr(thunk_atom_index);
268 const end_atom_index = if (thunk.len == 0) group_end else thunk.getEndAtomIndex();
269 const end_atom = macho_file.getAtomPtr(end_atom_index);
279270
280 if (thunk.len == 0) {
281 thunk.start_index = thunk_atom_index;
282 }
271 if (end_atom.next_index) |first_after_index| {
272 const first_after_atom = macho_file.getAtomPtr(first_after_index);
273 first_after_atom.prev_index = thunk_atom_index;
274 thunk_atom.next_index = first_after_index;
275 }
283276
284 thunk.len += 1;
285 }
277 end_atom.next_index = thunk_atom_index;
278 thunk_atom.prev_index = end_atom_index;
286279
287 try zld.thunk_table.put(gpa, atom_index, thunk_index);
280 if (thunk.len == 0) {
281 thunk.start_index = thunk_atom_index;
288282 }
283
284 thunk.len += 1;
285
286 return thunk_atom_index;
289287}
290288
291289inline fn relocNeedsThunk(rel: macho.relocation_info) bool {
......@@ -294,80 +292,78 @@ inline fn relocNeedsThunk(rel: macho.relocation_info) bool {
294292}
295293
296294fn isReachable(
297 zld: *Zld,
298 atom_index: AtomIndex,
295 macho_file: *MachO,
296 atom_index: Atom.Index,
299297 rel: macho.relocation_info,
300298 base_offset: i32,
301299 target: SymbolWithLoc,
302 allocated: std.AutoHashMap(AtomIndex, void),
300 allocated: std.AutoHashMap(Atom.Index, void),
303301) bool {
304 if (zld.getStubsAtomIndexForSymbol(target)) |_| return false;
302 if (macho_file.stub_table.lookup.contains(target)) return false;
305303
306 const source_atom = zld.getAtom(atom_index);
307 const source_sym = zld.getSymbol(source_atom.getSymbolWithLoc());
304 const source_atom = macho_file.getAtom(atom_index);
305 const source_sym = macho_file.getSymbol(source_atom.getSymbolWithLoc());
308306
309 const target_object = zld.objects.items[target.getFile().?];
307 const target_object = macho_file.objects.items[target.getFile().?];
310308 const target_atom_index = target_object.getAtomIndexForSymbol(target.sym_index).?;
311 const target_atom = zld.getAtom(target_atom_index);
312 const target_sym = zld.getSymbol(target_atom.getSymbolWithLoc());
309 const target_atom = macho_file.getAtom(target_atom_index);
310 const target_sym = macho_file.getSymbol(target_atom.getSymbolWithLoc());
313311
314312 if (source_sym.n_sect != target_sym.n_sect) return false;
315313
316314 if (!allocated.contains(target_atom_index)) return false;
317315
318316 const source_addr = source_sym.n_value + @as(u32, @intCast(rel.r_address - base_offset));
319 const is_via_got = Atom.relocRequiresGot(zld, rel);
320 const target_addr = Atom.getRelocTargetAddress(zld, target, is_via_got, false) catch unreachable;
317 const target_addr = if (Atom.relocRequiresGot(macho_file, rel))
318 macho_file.getGotEntryAddress(target).?
319 else
320 Atom.getRelocTargetAddress(macho_file, target, false);
321321 _ = Relocation.calcPcRelativeDisplacementArm64(source_addr, target_addr) catch
322322 return false;
323323
324324 return true;
325325}
326326
327fn createThunkAtom(zld: *Zld) !AtomIndex {
328 const sym_index = try zld.allocateSymbol();
329 const atom_index = try zld.createEmptyAtom(sym_index, @sizeOf(u32) * 3, 2);
330 const sym = zld.getSymbolPtr(.{ .sym_index = sym_index });
327fn createThunkAtom(macho_file: *MachO) !Atom.Index {
328 const sym_index = try macho_file.allocateSymbol();
329 const atom_index = try macho_file.createAtom(sym_index, .{ .size = @sizeOf(u32) * 3, .alignment = 2 });
330 const sym = macho_file.getSymbolPtr(.{ .sym_index = sym_index });
331331 sym.n_type = macho.N_SECT;
332
333 const sect_id = zld.getSectionByName("__TEXT", "__text") orelse unreachable;
334 sym.n_sect = sect_id + 1;
335
332 sym.n_sect = macho_file.text_section_index.? + 1;
336333 return atom_index;
337334}
338335
339fn getThunkIndex(zld: *Zld, atom_index: AtomIndex) ?ThunkIndex {
340 const atom = zld.getAtom(atom_index);
341 const sym = zld.getSymbol(atom.getSymbolWithLoc());
342 for (zld.thunks.items, 0..) |thunk, i| {
343 if (thunk.len == 0) continue;
344
345 const thunk_atom_index = thunk.getStartAtomIndex();
346 const thunk_atom = zld.getAtom(thunk_atom_index);
347 const thunk_sym = zld.getSymbol(thunk_atom.getSymbolWithLoc());
348 const start_addr = thunk_sym.n_value;
349 const end_addr = start_addr + thunk.getSize();
350
351 if (start_addr <= sym.n_value and sym.n_value < end_addr) {
352 return @as(u32, @intCast(i));
353 }
336pub fn writeThunkCode(macho_file: *MachO, thunk: *const Thunk, writer: anytype) !void {
337 const slice = thunk.targets.slice();
338 for (thunk.getStartAtomIndex()..thunk.getEndAtomIndex(), 0..) |atom_index, target_index| {
339 const atom = macho_file.getAtom(@intCast(atom_index));
340 const sym = macho_file.getSymbol(atom.getSymbolWithLoc());
341 const source_addr = sym.n_value;
342 const tag = slice.items(.tag)[target_index];
343 const target = slice.items(.target)[target_index];
344 const target_addr = switch (tag) {
345 .stub => macho_file.getStubsEntryAddress(target).?,
346 .atom => macho_file.getSymbol(target).n_value,
347 };
348 const pages = Relocation.calcNumberOfPages(source_addr, target_addr);
349 try writer.writeIntLittle(u32, aarch64.Instruction.adrp(.x16, pages).toU32());
350 const off = try Relocation.calcPageOffset(target_addr, .arithmetic);
351 try writer.writeIntLittle(u32, aarch64.Instruction.add(.x16, .x16, off, false).toU32());
352 try writer.writeIntLittle(u32, aarch64.Instruction.br(.x16).toU32());
354353 }
355 return null;
356354}
357355
358pub fn writeThunkCode(zld: *Zld, atom_index: AtomIndex, writer: anytype) !void {
359 const atom = zld.getAtom(atom_index);
360 const sym = zld.getSymbol(atom.getSymbolWithLoc());
361 const source_addr = sym.n_value;
362 const thunk = zld.thunks.items[getThunkIndex(zld, atom_index).?];
363 const target_addr = for (thunk.lookup.keys()) |target| {
364 const target_atom_index = thunk.lookup.get(target).?;
365 if (atom_index == target_atom_index) break zld.getSymbol(target).n_value;
366 } else unreachable;
367
368 const pages = Relocation.calcNumberOfPages(source_addr, target_addr);
369 try writer.writeIntLittle(u32, aarch64.Instruction.adrp(.x16, pages).toU32());
370 const off = try Relocation.calcPageOffset(target_addr, .arithmetic);
371 try writer.writeIntLittle(u32, aarch64.Instruction.add(.x16, .x16, off, false).toU32());
372 try writer.writeIntLittle(u32, aarch64.Instruction.br(.x16).toU32());
373}
356const std = @import("std");
357const assert = std.debug.assert;
358const log = std.log.scoped(.thunks);
359const macho = std.macho;
360const math = std.math;
361const mem = std.mem;
362
363const aarch64 = @import("../../arch/aarch64/bits.zig");
364
365const Allocator = mem.Allocator;
366const Atom = @import("Atom.zig");
367const MachO = @import("../MachO.zig");
368const Relocation = @import("Relocation.zig");
369const SymbolWithLoc = MachO.SymbolWithLoc;
src/link/MachO/uuid.zig+9-9
......@@ -1,12 +1,3 @@
1const std = @import("std");
2const fs = std.fs;
3const mem = std.mem;
4
5const Allocator = mem.Allocator;
6const Compilation = @import("../../Compilation.zig");
7const Md5 = std.crypto.hash.Md5;
8const Hasher = @import("hasher.zig").ParallelHasher;
9
101/// Calculates Md5 hash of each chunk in parallel and then hashes all Md5 hashes to produce
112/// the final digest.
123/// While this is NOT a correct MD5 hash of the contents, this methodology is used by LLVM/LLD
......@@ -43,3 +34,12 @@ inline fn conform(out: *[Md5.digest_length]u8) void {
4334 out[6] = (out[6] & 0x0F) | (3 << 4);
4435 out[8] = (out[8] & 0x3F) | 0x80;
4536}
37
38const std = @import("std");
39const fs = std.fs;
40const mem = std.mem;
41
42const Allocator = mem.Allocator;
43const Compilation = @import("../../Compilation.zig");
44const Md5 = std.crypto.hash.Md5;
45const Hasher = @import("hasher.zig").ParallelHasher;
src/link/MachO/zld.zig+1010-3719
......@@ -1,3940 +1,1231 @@
1const std = @import("std");
2const build_options = @import("build_options");
3const assert = std.debug.assert;
4const dwarf = std.dwarf;
5const fs = std.fs;
6const log = std.log.scoped(.link);
7const macho = std.macho;
8const math = std.math;
9const mem = std.mem;
10
11const aarch64 = @import("../../arch/aarch64/bits.zig");
12const calcUuid = @import("uuid.zig").calcUuid;
13const dead_strip = @import("dead_strip.zig");
14const eh_frame = @import("eh_frame.zig");
15const fat = @import("fat.zig");
16const link = @import("../../link.zig");
17const load_commands = @import("load_commands.zig");
18const stub_helpers = @import("stubs.zig");
19const thunks = @import("thunks.zig");
20const trace = @import("../../tracy.zig").trace;
21
22const Allocator = mem.Allocator;
23const Archive = @import("Archive.zig");
24const Atom = @import("ZldAtom.zig");
25const Cache = std.Build.Cache;
26const CodeSignature = @import("CodeSignature.zig");
27const Compilation = @import("../../Compilation.zig");
28const DwarfInfo = @import("DwarfInfo.zig");
29const Dylib = @import("Dylib.zig");
30const MachO = @import("../MachO.zig");
31const Md5 = std.crypto.hash.Md5;
32const LibStub = @import("../tapi.zig").LibStub;
33const Object = @import("Object.zig");
34const StringTable = @import("../strtab.zig").StringTable;
35const Trie = @import("Trie.zig");
36const UnwindInfo = @import("UnwindInfo.zig");
37
38const Bind = @import("dyld_info/bind.zig").Bind(*const Zld, SymbolWithLoc);
39const LazyBind = @import("dyld_info/bind.zig").LazyBind(*const Zld, SymbolWithLoc);
40const Rebase = @import("dyld_info/Rebase.zig");
41
42pub const Zld = struct {
43 gpa: Allocator,
44 file: fs.File,
45 page_size: u16,
46 options: *const link.Options,
47
48 dyld_info_cmd: macho.dyld_info_command = .{},
49 symtab_cmd: macho.symtab_command = .{},
50 dysymtab_cmd: macho.dysymtab_command = .{},
51 function_starts_cmd: macho.linkedit_data_command = .{ .cmd = .FUNCTION_STARTS },
52 data_in_code_cmd: macho.linkedit_data_command = .{ .cmd = .DATA_IN_CODE },
53 uuid_cmd: macho.uuid_command = .{
54 .uuid = [_]u8{0} ** 16,
55 },
56 codesig_cmd: macho.linkedit_data_command = .{ .cmd = .CODE_SIGNATURE },
57
58 objects: std.ArrayListUnmanaged(Object) = .{},
59 archives: std.ArrayListUnmanaged(Archive) = .{},
60 dylibs: std.ArrayListUnmanaged(Dylib) = .{},
61 dylibs_map: std.StringHashMapUnmanaged(u16) = .{},
62 referenced_dylibs: std.AutoArrayHashMapUnmanaged(u16, void) = .{},
63
64 segments: std.ArrayListUnmanaged(macho.segment_command_64) = .{},
65 sections: std.MultiArrayList(Section) = .{},
66
67 locals: std.ArrayListUnmanaged(macho.nlist_64) = .{},
68 globals: std.ArrayListUnmanaged(SymbolWithLoc) = .{},
69
70 entry_index: ?u32 = null,
71 mh_execute_header_index: ?u32 = null,
72 dso_handle_index: ?u32 = null,
73 dyld_stub_binder_index: ?u32 = null,
74 dyld_private_sym_index: ?u32 = null,
75 stub_helper_preamble_sym_index: ?u32 = null,
76
77 strtab: StringTable(.strtab) = .{},
78
79 tlv_ptr_entries: std.ArrayListUnmanaged(IndirectPointer) = .{},
80 tlv_ptr_table: std.AutoHashMapUnmanaged(SymbolWithLoc, u32) = .{},
81
82 got_entries: std.ArrayListUnmanaged(IndirectPointer) = .{},
83 got_table: std.AutoHashMapUnmanaged(SymbolWithLoc, u32) = .{},
84
85 stubs: std.ArrayListUnmanaged(IndirectPointer) = .{},
86 stubs_table: std.AutoHashMapUnmanaged(SymbolWithLoc, u32) = .{},
87
88 thunk_table: std.AutoHashMapUnmanaged(AtomIndex, thunks.ThunkIndex) = .{},
89 thunks: std.ArrayListUnmanaged(thunks.Thunk) = .{},
90
91 atoms: std.ArrayListUnmanaged(Atom) = .{},
92
93 fn parseObject(self: *Zld, path: []const u8) !bool {
94 const gpa = self.gpa;
95 const file = fs.cwd().openFile(path, .{}) catch |err| switch (err) {
96 error.FileNotFound => return false,
97 else => |e| return e,
98 };
99 defer file.close();
100
101 const name = try gpa.dupe(u8, path);
102 errdefer gpa.free(name);
103 const cpu_arch = self.options.target.cpu.arch;
104 const mtime: u64 = mtime: {
105 const stat = file.stat() catch break :mtime 0;
106 break :mtime @as(u64, @intCast(@divFloor(stat.mtime, 1_000_000_000)));
107 };
108 const file_stat = try file.stat();
109 const file_size = math.cast(usize, file_stat.size) orelse return error.Overflow;
110 const contents = try file.readToEndAllocOptions(gpa, file_size, file_size, @alignOf(u64), null);
111
112 var object = Object{
113 .name = name,
114 .mtime = mtime,
115 .contents = contents,
116 };
117
118 object.parse(gpa, cpu_arch) catch |err| switch (err) {
119 error.EndOfStream, error.NotObject => {
120 object.deinit(gpa);
121 return false;
122 },
123 else => |e| return e,
124 };
125
126 try self.objects.append(gpa, object);
127
128 return true;
129 }
130
131 fn parseArchive(self: *Zld, path: []const u8, force_load: bool) !bool {
132 const gpa = self.gpa;
133 const file = fs.cwd().openFile(path, .{}) catch |err| switch (err) {
134 error.FileNotFound => return false,
135 else => |e| return e,
136 };
137 errdefer file.close();
138
139 const name = try gpa.dupe(u8, path);
140 errdefer gpa.free(name);
141 const cpu_arch = self.options.target.cpu.arch;
142 const reader = file.reader();
143 const fat_offset = try fat.getLibraryOffset(reader, cpu_arch);
144 try reader.context.seekTo(fat_offset);
145
146 var archive = Archive{
147 .name = name,
148 .fat_offset = fat_offset,
149 .file = file,
150 };
151
152 archive.parse(gpa, reader) catch |err| switch (err) {
153 error.EndOfStream, error.NotArchive => {
154 archive.deinit(gpa);
155 return false;
156 },
157 else => |e| return e,
158 };
159
160 if (force_load) {
161 defer archive.deinit(gpa);
162 // Get all offsets from the ToC
163 var offsets = std.AutoArrayHashMap(u32, void).init(gpa);
164 defer offsets.deinit();
165 for (archive.toc.values()) |offs| {
166 for (offs.items) |off| {
167 _ = try offsets.getOrPut(off);
168 }
169 }
170 for (offsets.keys()) |off| {
171 const object = try archive.parseObject(gpa, cpu_arch, off);
172 try self.objects.append(gpa, object);
173 }
174 } else {
175 try self.archives.append(gpa, archive);
176 }
177
178 return true;
179 }
180
181 const ParseDylibError = error{
182 OutOfMemory,
183 EmptyStubFile,
184 MismatchedCpuArchitecture,
185 UnsupportedCpuArchitecture,
186 EndOfStream,
187 } || fs.File.OpenError || std.os.PReadError || Dylib.Id.ParseError;
188
189 const DylibCreateOpts = struct {
190 syslibroot: ?[]const u8,
191 id: ?Dylib.Id = null,
192 dependent: bool = false,
193 needed: bool = false,
194 weak: bool = false,
195 };
196
197 fn parseDylib(
198 self: *Zld,
199 path: []const u8,
200 dependent_libs: anytype,
201 opts: DylibCreateOpts,
202 ) ParseDylibError!bool {
203 const gpa = self.gpa;
204 const file = fs.cwd().openFile(path, .{}) catch |err| switch (err) {
205 error.FileNotFound => return false,
206 else => |e| return e,
207 };
208 defer file.close();
209
210 const cpu_arch = self.options.target.cpu.arch;
211 const file_stat = try file.stat();
212 var file_size = math.cast(usize, file_stat.size) orelse return error.Overflow;
213
214 const reader = file.reader();
215 const fat_offset = math.cast(usize, try fat.getLibraryOffset(reader, cpu_arch)) orelse
216 return error.Overflow;
217 try file.seekTo(fat_offset);
218 file_size -= fat_offset;
219
220 const contents = try file.readToEndAllocOptions(gpa, file_size, file_size, @alignOf(u64), null);
221 defer gpa.free(contents);
222
223 const dylib_id = @as(u16, @intCast(self.dylibs.items.len));
224 var dylib = Dylib{ .weak = opts.weak };
225
226 dylib.parseFromBinary(
227 gpa,
228 cpu_arch,
229 dylib_id,
230 dependent_libs,
231 path,
232 contents,
233 ) catch |err| switch (err) {
234 error.EndOfStream, error.NotDylib => {
235 try file.seekTo(0);
236
237 var lib_stub = LibStub.loadFromFile(gpa, file) catch {
238 dylib.deinit(gpa);
239 return false;
240 };
241 defer lib_stub.deinit();
242
243 try dylib.parseFromStub(
244 gpa,
245 self.options.target,
246 lib_stub,
247 dylib_id,
248 dependent_libs,
249 path,
250 );
251 },
252 else => |e| return e,
253 };
254
255 if (opts.id) |id| {
256 if (dylib.id.?.current_version < id.compatibility_version) {
257 log.warn("found dylib is incompatible with the required minimum version", .{});
258 log.warn(" dylib: {s}", .{id.name});
259 log.warn(" required minimum version: {}", .{id.compatibility_version});
260 log.warn(" dylib version: {}", .{dylib.id.?.current_version});
261
262 // TODO maybe this should be an error and facilitate auto-cleanup?
263 dylib.deinit(gpa);
264 return false;
265 }
266 }
267
268 try self.dylibs.append(gpa, dylib);
269 try self.dylibs_map.putNoClobber(gpa, dylib.id.?.name, dylib_id);
270
271 const should_link_dylib_even_if_unreachable = blk: {
272 if (self.options.dead_strip_dylibs and !opts.needed) break :blk false;
273 break :blk !(opts.dependent or self.referenced_dylibs.contains(dylib_id));
274 };
275
276 if (should_link_dylib_even_if_unreachable) {
277 try self.referenced_dylibs.putNoClobber(gpa, dylib_id, {});
278 }
279
280 return true;
281 }
282
283 fn parseInputFiles(
284 self: *Zld,
285 files: []const []const u8,
286 syslibroot: ?[]const u8,
287 dependent_libs: anytype,
288 ) !void {
289 for (files) |file_name| {
290 const full_path = full_path: {
291 var buffer: [fs.MAX_PATH_BYTES]u8 = undefined;
292 break :full_path try fs.realpath(file_name, &buffer);
293 };
294 log.debug("parsing input file path '{s}'", .{full_path});
295
296 if (try self.parseObject(full_path)) continue;
297 if (try self.parseArchive(full_path, false)) continue;
298 if (try self.parseDylib(full_path, dependent_libs, .{
299 .syslibroot = syslibroot,
300 })) continue;
301
302 log.debug("unknown filetype for positional input file: '{s}'", .{file_name});
303 }
304 }
305
306 fn parseAndForceLoadStaticArchives(self: *Zld, files: []const []const u8) !void {
307 for (files) |file_name| {
308 const full_path = full_path: {
309 var buffer: [fs.MAX_PATH_BYTES]u8 = undefined;
310 break :full_path try fs.realpath(file_name, &buffer);
311 };
312 log.debug("parsing and force loading static archive '{s}'", .{full_path});
313
314 if (try self.parseArchive(full_path, true)) continue;
315 log.debug("unknown filetype: expected static archive: '{s}'", .{file_name});
316 }
317 }
318
319 fn parseLibs(
320 self: *Zld,
321 lib_names: []const []const u8,
322 lib_infos: []const link.SystemLib,
323 syslibroot: ?[]const u8,
324 dependent_libs: anytype,
325 ) !void {
326 for (lib_names, 0..) |lib, i| {
327 const lib_info = lib_infos[i];
328 log.debug("parsing lib path '{s}'", .{lib});
329 if (try self.parseDylib(lib, dependent_libs, .{
330 .syslibroot = syslibroot,
331 .needed = lib_info.needed,
332 .weak = lib_info.weak,
333 })) continue;
334 if (try self.parseArchive(lib, false)) continue;
335
336 log.debug("unknown filetype for a library: '{s}'", .{lib});
337 }
338 }
339
340 fn parseDependentLibs(self: *Zld, syslibroot: ?[]const u8, dependent_libs: anytype) !void {
341 // At this point, we can now parse dependents of dylibs preserving the inclusion order of:
342 // 1) anything on the linker line is parsed first
343 // 2) afterwards, we parse dependents of the included dylibs
344 // TODO this should not be performed if the user specifies `-flat_namespace` flag.
345 // See ld64 manpages.
346 var arena_alloc = std.heap.ArenaAllocator.init(self.gpa);
347 const arena = arena_alloc.allocator();
348 defer arena_alloc.deinit();
349
350 while (dependent_libs.readItem()) |*dep_id| {
351 defer dep_id.id.deinit(self.gpa);
352
353 if (self.dylibs_map.contains(dep_id.id.name)) continue;
354
355 const weak = self.dylibs.items[dep_id.parent].weak;
356 const has_ext = blk: {
357 const basename = fs.path.basename(dep_id.id.name);
358 break :blk mem.lastIndexOfScalar(u8, basename, '.') != null;
359 };
360 const extension = if (has_ext) fs.path.extension(dep_id.id.name) else "";
361 const without_ext = if (has_ext) blk: {
362 const index = mem.lastIndexOfScalar(u8, dep_id.id.name, '.') orelse unreachable;
363 break :blk dep_id.id.name[0..index];
364 } else dep_id.id.name;
365
366 for (&[_][]const u8{ extension, ".tbd" }) |ext| {
367 const with_ext = try std.fmt.allocPrint(arena, "{s}{s}", .{ without_ext, ext });
368 const full_path = if (syslibroot) |root| try fs.path.join(arena, &.{ root, with_ext }) else with_ext;
369
370 log.debug("trying dependency at fully resolved path {s}", .{full_path});
371
372 const did_parse_successfully = try self.parseDylib(full_path, dependent_libs, .{
373 .id = dep_id.id,
374 .syslibroot = syslibroot,
375 .dependent = true,
376 .weak = weak,
377 });
378 if (did_parse_successfully) break;
379 } else {
380 log.debug("unable to resolve dependency {s}", .{dep_id.id.name});
381 }
382 }
383 }
384
385 pub fn getOutputSection(self: *Zld, sect: macho.section_64) !?u8 {
386 const segname = sect.segName();
387 const sectname = sect.sectName();
388 const res: ?u8 = blk: {
389 if (mem.eql(u8, "__LLVM", segname)) {
390 log.debug("TODO LLVM section: type 0x{x}, name '{s},{s}'", .{
391 sect.flags, segname, sectname,
392 });
393 break :blk null;
394 }
395
396 // We handle unwind info separately.
397 if (mem.eql(u8, "__TEXT", segname) and mem.eql(u8, "__eh_frame", sectname)) {
398 break :blk null;
399 }
400 if (mem.eql(u8, "__LD", segname) and mem.eql(u8, "__compact_unwind", sectname)) {
401 break :blk null;
402 }
403
404 if (sect.isCode()) {
405 break :blk self.getSectionByName("__TEXT", "__text") orelse try self.initSection(
406 "__TEXT",
407 "__text",
408 .{
409 .flags = macho.S_REGULAR |
410 macho.S_ATTR_PURE_INSTRUCTIONS |
411 macho.S_ATTR_SOME_INSTRUCTIONS,
412 },
413 );
414 }
415
416 if (sect.isDebug()) {
417 break :blk null;
418 }
419
420 switch (sect.type()) {
421 macho.S_4BYTE_LITERALS,
422 macho.S_8BYTE_LITERALS,
423 macho.S_16BYTE_LITERALS,
424 => {
425 break :blk self.getSectionByName("__TEXT", "__const") orelse try self.initSection(
426 "__TEXT",
427 "__const",
428 .{},
429 );
430 },
431 macho.S_CSTRING_LITERALS => {
432 if (mem.startsWith(u8, sectname, "__objc")) {
433 break :blk self.getSectionByName(segname, sectname) orelse try self.initSection(
434 segname,
435 sectname,
436 .{},
437 );
438 }
439 break :blk self.getSectionByName("__TEXT", "__cstring") orelse try self.initSection(
440 "__TEXT",
441 "__cstring",
442 .{ .flags = macho.S_CSTRING_LITERALS },
443 );
444 },
445 macho.S_MOD_INIT_FUNC_POINTERS,
446 macho.S_MOD_TERM_FUNC_POINTERS,
447 => {
448 break :blk self.getSectionByName("__DATA_CONST", sectname) orelse try self.initSection(
449 "__DATA_CONST",
450 sectname,
451 .{ .flags = sect.flags },
452 );
453 },
454 macho.S_LITERAL_POINTERS,
455 macho.S_ZEROFILL,
456 macho.S_THREAD_LOCAL_VARIABLES,
457 macho.S_THREAD_LOCAL_VARIABLE_POINTERS,
458 macho.S_THREAD_LOCAL_REGULAR,
459 macho.S_THREAD_LOCAL_ZEROFILL,
460 => {
461 break :blk self.getSectionByName(segname, sectname) orelse try self.initSection(
462 segname,
463 sectname,
464 .{ .flags = sect.flags },
465 );
466 },
467 macho.S_COALESCED => {
468 break :blk self.getSectionByName(segname, sectname) orelse try self.initSection(
469 segname,
470 sectname,
471 .{},
472 );
473 },
474 macho.S_REGULAR => {
475 if (mem.eql(u8, segname, "__TEXT")) {
476 if (mem.eql(u8, sectname, "__rodata") or
477 mem.eql(u8, sectname, "__typelink") or
478 mem.eql(u8, sectname, "__itablink") or
479 mem.eql(u8, sectname, "__gosymtab") or
480 mem.eql(u8, sectname, "__gopclntab"))
481 {
482 break :blk self.getSectionByName("__TEXT", sectname) orelse try self.initSection(
483 "__TEXT",
484 sectname,
485 .{},
486 );
487 }
488 }
489 if (mem.eql(u8, segname, "__DATA")) {
490 if (mem.eql(u8, sectname, "__const") or
491 mem.eql(u8, sectname, "__cfstring") or
492 mem.eql(u8, sectname, "__objc_classlist") or
493 mem.eql(u8, sectname, "__objc_imageinfo"))
494 {
495 break :blk self.getSectionByName("__DATA_CONST", sectname) orelse try self.initSection(
496 "__DATA_CONST",
497 sectname,
498 .{},
499 );
500 } else if (mem.eql(u8, sectname, "__data")) {
501 break :blk self.getSectionByName("__DATA", "__data") orelse try self.initSection(
502 "__DATA",
503 "__data",
504 .{},
505 );
506 }
507 }
508 break :blk self.getSectionByName(segname, sectname) orelse try self.initSection(
509 segname,
510 sectname,
511 .{},
512 );
513 },
514 else => break :blk null,
515 }
516 };
517 return res;
518 }
519
520 pub fn addAtomToSection(self: *Zld, atom_index: AtomIndex) void {
521 const atom = self.getAtomPtr(atom_index);
522 const sym = self.getSymbol(atom.getSymbolWithLoc());
523 var section = self.sections.get(sym.n_sect - 1);
524 if (section.header.size > 0) {
525 const last_atom = self.getAtomPtr(section.last_atom_index);
526 last_atom.next_index = atom_index;
527 atom.prev_index = section.last_atom_index;
528 } else {
529 section.first_atom_index = atom_index;
530 }
531 section.last_atom_index = atom_index;
532 section.header.size += atom.size;
533 self.sections.set(sym.n_sect - 1, section);
534 }
535
536 pub fn createEmptyAtom(self: *Zld, sym_index: u32, size: u64, alignment: u32) !AtomIndex {
537 const gpa = self.gpa;
538 const index = @as(AtomIndex, @intCast(self.atoms.items.len));
539 const atom = try self.atoms.addOne(gpa);
540 atom.* = Atom.empty;
541 atom.sym_index = sym_index;
542 atom.size = size;
543 atom.alignment = alignment;
544
545 log.debug("creating ATOM(%{d}) at index {d}", .{ sym_index, index });
546
547 return index;
548 }
549
550 pub fn createGotAtom(self: *Zld) !AtomIndex {
551 const sym_index = try self.allocateSymbol();
552 const atom_index = try self.createEmptyAtom(sym_index, @sizeOf(u64), 3);
553 const sym = self.getSymbolPtr(.{ .sym_index = sym_index });
554 sym.n_type = macho.N_SECT;
555
556 const sect_id = self.getSectionByName("__DATA_CONST", "__got") orelse
557 try self.initSection("__DATA_CONST", "__got", .{
558 .flags = macho.S_NON_LAZY_SYMBOL_POINTERS,
559 });
560 sym.n_sect = sect_id + 1;
561
562 self.addAtomToSection(atom_index);
563
564 return atom_index;
565 }
566
567 fn writeGotPointer(self: *Zld, got_index: u32, writer: anytype) !void {
568 const target_addr = blk: {
569 const entry = self.got_entries.items[got_index];
570 const sym = entry.getTargetSymbol(self);
571 break :blk sym.n_value;
572 };
573 try writer.writeIntLittle(u64, target_addr);
574 }
575
576 pub fn createTlvPtrAtom(self: *Zld) !AtomIndex {
577 const sym_index = try self.allocateSymbol();
578 const atom_index = try self.createEmptyAtom(sym_index, @sizeOf(u64), 3);
579 const sym = self.getSymbolPtr(.{ .sym_index = sym_index });
580 sym.n_type = macho.N_SECT;
581
582 const sect_id = (try self.getOutputSection(.{
583 .segname = makeStaticString("__DATA"),
584 .sectname = makeStaticString("__thread_ptrs"),
585 .flags = macho.S_THREAD_LOCAL_VARIABLE_POINTERS,
586 })).?;
587 sym.n_sect = sect_id + 1;
588
589 self.addAtomToSection(atom_index);
590
591 return atom_index;
592 }
593
594 fn createDyldStubBinderGotAtom(self: *Zld) !void {
595 const gpa = self.gpa;
596 const global_index = self.dyld_stub_binder_index orelse return;
597 const target = self.globals.items[global_index];
598 const atom_index = try self.createGotAtom();
599 const got_index = @as(u32, @intCast(self.got_entries.items.len));
600 try self.got_entries.append(gpa, .{
601 .target = target,
602 .atom_index = atom_index,
603 });
604 try self.got_table.putNoClobber(gpa, target, got_index);
605 }
606
607 fn createDyldPrivateAtom(self: *Zld) !void {
608 if (self.dyld_stub_binder_index == null) return;
609
610 const sym_index = try self.allocateSymbol();
611 const atom_index = try self.createEmptyAtom(sym_index, @sizeOf(u64), 3);
612 const sym = self.getSymbolPtr(.{ .sym_index = sym_index });
613 sym.n_type = macho.N_SECT;
614
615 const sect_id = self.getSectionByName("__DATA", "__data") orelse try self.initSection("__DATA", "__data", .{});
616 sym.n_sect = sect_id + 1;
617
618 self.dyld_private_sym_index = sym_index;
619
620 self.addAtomToSection(atom_index);
621 }
622
623 fn createStubHelperPreambleAtom(self: *Zld) !void {
624 if (self.dyld_stub_binder_index == null) return;
625
626 const cpu_arch = self.options.target.cpu.arch;
627 const size: u64 = switch (cpu_arch) {
628 .x86_64 => 15,
629 .aarch64 => 6 * @sizeOf(u32),
630 else => unreachable,
631 };
632 const alignment: u32 = switch (cpu_arch) {
633 .x86_64 => 0,
634 .aarch64 => 2,
635 else => unreachable,
636 };
637 const sym_index = try self.allocateSymbol();
638 const atom_index = try self.createEmptyAtom(sym_index, size, alignment);
639 const sym = self.getSymbolPtr(.{ .sym_index = sym_index });
640 sym.n_type = macho.N_SECT;
641
642 const sect_id = self.getSectionByName("__TEXT", "__stub_helper") orelse
643 try self.initSection("__TEXT", "__stub_helper", .{
644 .flags = macho.S_REGULAR |
645 macho.S_ATTR_PURE_INSTRUCTIONS |
646 macho.S_ATTR_SOME_INSTRUCTIONS,
647 });
648 sym.n_sect = sect_id + 1;
649
650 self.stub_helper_preamble_sym_index = sym_index;
651
652 self.addAtomToSection(atom_index);
653 }
654
655 fn writeStubHelperPreambleCode(self: *Zld, writer: anytype) !void {
656 const cpu_arch = self.options.target.cpu.arch;
657 const source_addr = blk: {
658 const sym = self.getSymbol(.{ .sym_index = self.stub_helper_preamble_sym_index.? });
659 break :blk sym.n_value;
660 };
661 const dyld_private_addr = blk: {
662 const sym = self.getSymbol(.{ .sym_index = self.dyld_private_sym_index.? });
663 break :blk sym.n_value;
664 };
665 const dyld_stub_binder_got_addr = blk: {
666 const sym_loc = self.globals.items[self.dyld_stub_binder_index.?];
667 const index = self.got_table.get(sym_loc).?;
668 const entry = self.got_entries.items[index];
669 break :blk entry.getAtomSymbol(self).n_value;
670 };
671 try stub_helpers.writeStubHelperPreambleCode(.{
672 .cpu_arch = cpu_arch,
673 .source_addr = source_addr,
674 .dyld_private_addr = dyld_private_addr,
675 .dyld_stub_binder_got_addr = dyld_stub_binder_got_addr,
676 }, writer);
677 }
678
679 pub fn createStubHelperAtom(self: *Zld) !AtomIndex {
680 const cpu_arch = self.options.target.cpu.arch;
681 const stub_size = stub_helpers.calcStubHelperEntrySize(cpu_arch);
682 const alignment: u2 = switch (cpu_arch) {
683 .x86_64 => 0,
684 .aarch64 => 2,
685 else => unreachable,
686 };
687
688 const sym_index = try self.allocateSymbol();
689 const atom_index = try self.createEmptyAtom(sym_index, stub_size, alignment);
690 const sym = self.getSymbolPtr(.{ .sym_index = sym_index });
691 sym.n_sect = macho.N_SECT;
692
693 const sect_id = self.getSectionByName("__TEXT", "__stub_helper").?;
694 sym.n_sect = sect_id + 1;
695
696 self.addAtomToSection(atom_index);
697
698 return atom_index;
699 }
700
701 fn writeStubHelperCode(self: *Zld, atom_index: AtomIndex, writer: anytype) !void {
702 const cpu_arch = self.options.target.cpu.arch;
703 const source_addr = blk: {
704 const atom = self.getAtom(atom_index);
705 const sym = self.getSymbol(atom.getSymbolWithLoc());
706 break :blk sym.n_value;
707 };
708 const target_addr = blk: {
709 const sym = self.getSymbol(.{ .sym_index = self.stub_helper_preamble_sym_index.? });
710 break :blk sym.n_value;
711 };
712 try stub_helpers.writeStubHelperCode(.{
713 .cpu_arch = cpu_arch,
714 .source_addr = source_addr,
715 .target_addr = target_addr,
716 }, writer);
717 }
718
719 pub fn createLazyPointerAtom(self: *Zld) !AtomIndex {
720 const sym_index = try self.allocateSymbol();
721 const atom_index = try self.createEmptyAtom(sym_index, @sizeOf(u64), 3);
722 const sym = self.getSymbolPtr(.{ .sym_index = sym_index });
723 sym.n_type = macho.N_SECT;
724
725 const sect_id = self.getSectionByName("__DATA", "__la_symbol_ptr") orelse
726 try self.initSection("__DATA", "__la_symbol_ptr", .{
727 .flags = macho.S_LAZY_SYMBOL_POINTERS,
728 });
729 sym.n_sect = sect_id + 1;
730
731 self.addAtomToSection(atom_index);
732
733 return atom_index;
734 }
735
736 fn writeLazyPointer(self: *Zld, stub_helper_index: u32, writer: anytype) !void {
737 const target_addr = blk: {
738 const sect_id = self.getSectionByName("__TEXT", "__stub_helper").?;
739 var atom_index = self.sections.items(.first_atom_index)[sect_id];
740 var count: u32 = 0;
741 while (count < stub_helper_index + 1) : (count += 1) {
742 const atom = self.getAtom(atom_index);
743 if (atom.next_index) |next_index| {
744 atom_index = next_index;
745 }
746 }
747 const atom = self.getAtom(atom_index);
748 const sym = self.getSymbol(atom.getSymbolWithLoc());
749 break :blk sym.n_value;
750 };
751 try writer.writeIntLittle(u64, target_addr);
752 }
753
754 pub fn createStubAtom(self: *Zld) !AtomIndex {
755 const cpu_arch = self.options.target.cpu.arch;
756 const alignment: u2 = switch (cpu_arch) {
757 .x86_64 => 0,
758 .aarch64 => 2,
759 else => unreachable, // unhandled architecture type
760 };
761 const stub_size = stub_helpers.calcStubEntrySize(cpu_arch);
762 const sym_index = try self.allocateSymbol();
763 const atom_index = try self.createEmptyAtom(sym_index, stub_size, alignment);
764 const sym = self.getSymbolPtr(.{ .sym_index = sym_index });
765 sym.n_type = macho.N_SECT;
766
767 const sect_id = self.getSectionByName("__TEXT", "__stubs") orelse
768 try self.initSection("__TEXT", "__stubs", .{
769 .flags = macho.S_SYMBOL_STUBS |
770 macho.S_ATTR_PURE_INSTRUCTIONS |
771 macho.S_ATTR_SOME_INSTRUCTIONS,
772 .reserved2 = stub_size,
773 });
774 sym.n_sect = sect_id + 1;
775
776 self.addAtomToSection(atom_index);
777
778 return atom_index;
779 }
780
781 fn writeStubCode(self: *Zld, atom_index: AtomIndex, stub_index: u32, writer: anytype) !void {
782 const cpu_arch = self.options.target.cpu.arch;
783 const source_addr = blk: {
784 const atom = self.getAtom(atom_index);
785 const sym = self.getSymbol(atom.getSymbolWithLoc());
786 break :blk sym.n_value;
787 };
788 const target_addr = blk: {
789 // TODO: cache this at stub atom creation; they always go in pairs anyhow
790 const la_sect_id = self.getSectionByName("__DATA", "__la_symbol_ptr").?;
791 var la_atom_index = self.sections.items(.first_atom_index)[la_sect_id];
792 var count: u32 = 0;
793 while (count < stub_index) : (count += 1) {
794 const la_atom = self.getAtom(la_atom_index);
795 la_atom_index = la_atom.next_index.?;
796 }
797 const atom = self.getAtom(la_atom_index);
798 const sym = self.getSymbol(atom.getSymbolWithLoc());
799 break :blk sym.n_value;
800 };
801 try stub_helpers.writeStubCode(.{
802 .cpu_arch = cpu_arch,
803 .source_addr = source_addr,
804 .target_addr = target_addr,
805 }, writer);
806 }
807
808 fn createTentativeDefAtoms(self: *Zld) !void {
809 const gpa = self.gpa;
810
811 for (self.globals.items) |global| {
812 const sym = self.getSymbolPtr(global);
813 if (!sym.tentative()) continue;
814 if (sym.n_desc == N_DEAD) continue;
815
816 log.debug("creating tentative definition for ATOM(%{d}, '{s}') in object({?})", .{
817 global.sym_index, self.getSymbolName(global), global.file,
818 });
819
820 // Convert any tentative definition into a regular symbol and allocate
821 // text blocks for each tentative definition.
822 const size = sym.n_value;
823 const alignment = (sym.n_desc >> 8) & 0x0f;
824 const n_sect = (try self.getOutputSection(.{
825 .segname = makeStaticString("__DATA"),
826 .sectname = makeStaticString("__bss"),
827 .flags = macho.S_ZEROFILL,
828 })).? + 1;
829
830 sym.* = .{
831 .n_strx = sym.n_strx,
832 .n_type = macho.N_SECT | macho.N_EXT,
833 .n_sect = n_sect,
834 .n_desc = 0,
835 .n_value = 0,
836 };
837
838 const atom_index = try self.createEmptyAtom(global.sym_index, size, alignment);
839 const atom = self.getAtomPtr(atom_index);
840 atom.file = global.file;
841
842 self.addAtomToSection(atom_index);
843
844 assert(global.getFile() != null);
845 const object = &self.objects.items[global.getFile().?];
846 try object.atoms.append(gpa, atom_index);
847 object.atom_by_index_table[global.sym_index] = atom_index;
848 }
849 }
850
851 fn addUndefined(self: *Zld, name: []const u8, resolver: *SymbolResolver) !void {
852 const sym_index = try self.allocateSymbol();
853 const sym_loc = SymbolWithLoc{ .sym_index = sym_index };
854 const sym = self.getSymbolPtr(sym_loc);
855 sym.n_strx = try self.strtab.insert(self.gpa, name);
856 sym.n_type = macho.N_UNDF;
857 const global_index = try self.addGlobal(sym_loc);
858 try resolver.table.putNoClobber(name, global_index);
859 try resolver.unresolved.putNoClobber(global_index, {});
860 }
861
862 fn resolveSymbols(self: *Zld, resolver: *SymbolResolver) !void {
863 // We add the specified entrypoint as the first unresolved symbols so that
864 // we search for it in libraries should there be no object files specified
865 // on the linker line.
866 if (self.options.output_mode == .Exe) {
867 const entry_name = self.options.entry orelse load_commands.default_entry_point;
868 try self.addUndefined(entry_name, resolver);
869 }
870
871 // Force resolution of any symbols requested by the user.
872 for (self.options.force_undefined_symbols.keys()) |sym_name| {
873 try self.addUndefined(sym_name, resolver);
874 }
875
876 for (self.objects.items, 0..) |_, object_id| {
877 try self.resolveSymbolsInObject(@as(u32, @intCast(object_id)), resolver);
878 }
879
880 try self.resolveSymbolsInArchives(resolver);
881
882 // Finally, force resolution of dyld_stub_binder if there are imports
883 // requested.
884 if (resolver.unresolved.count() > 0) {
885 try self.addUndefined("dyld_stub_binder", resolver);
886 }
887
888 try self.resolveSymbolsInDylibs(resolver);
889
890 self.dyld_stub_binder_index = resolver.table.get("dyld_stub_binder");
891
892 try self.createMhExecuteHeaderSymbol(resolver);
893 try self.createDsoHandleSymbol(resolver);
894 try self.resolveSymbolsAtLoading(resolver);
895 }
896
897 fn resolveSymbolsInObject(self: *Zld, object_id: u32, resolver: *SymbolResolver) !void {
898 const object = &self.objects.items[object_id];
899 const in_symtab = object.in_symtab orelse return;
900
901 log.debug("resolving symbols in '{s}'", .{object.name});
902
903 var sym_index: u32 = 0;
904 while (sym_index < in_symtab.len) : (sym_index += 1) {
905 const sym = &object.symtab[sym_index];
906 const sym_name = object.getSymbolName(sym_index);
907
908 if (sym.stab()) {
909 log.err("unhandled symbol type: stab", .{});
910 log.err(" symbol '{s}'", .{sym_name});
911 log.err(" first definition in '{s}'", .{object.name});
912 return error.UnhandledSymbolType;
913 }
914
915 if (sym.indr()) {
916 log.err("unhandled symbol type: indirect", .{});
917 log.err(" symbol '{s}'", .{sym_name});
918 log.err(" first definition in '{s}'", .{object.name});
919 return error.UnhandledSymbolType;
920 }
921
922 if (sym.abs()) {
923 log.err("unhandled symbol type: absolute", .{});
924 log.err(" symbol '{s}'", .{sym_name});
925 log.err(" first definition in '{s}'", .{object.name});
926 return error.UnhandledSymbolType;
927 }
928
929 if (sym.sect() and !sym.ext()) {
930 log.debug("symbol '{s}' local to object {s}; skipping...", .{
931 sym_name,
932 object.name,
933 });
934 continue;
935 }
936
937 const sym_loc = SymbolWithLoc{ .sym_index = sym_index, .file = object_id + 1 };
938
939 const global_index = resolver.table.get(sym_name) orelse {
940 const global_index = try self.addGlobal(sym_loc);
941 try resolver.table.putNoClobber(sym_name, global_index);
942 if (sym.undf() and !sym.tentative()) {
943 try resolver.unresolved.putNoClobber(global_index, {});
944 }
945 continue;
946 };
947 const global = &self.globals.items[global_index];
948 const global_sym = self.getSymbol(global.*);
949
950 // Cases to consider: sym vs global_sym
951 // 1. strong(sym) and strong(global_sym) => error
952 // 2. strong(sym) and weak(global_sym) => sym
953 // 3. strong(sym) and tentative(global_sym) => sym
954 // 4. strong(sym) and undf(global_sym) => sym
955 // 5. weak(sym) and strong(global_sym) => global_sym
956 // 6. weak(sym) and tentative(global_sym) => sym
957 // 7. weak(sym) and undf(global_sym) => sym
958 // 8. tentative(sym) and strong(global_sym) => global_sym
959 // 9. tentative(sym) and weak(global_sym) => global_sym
960 // 10. tentative(sym) and tentative(global_sym) => pick larger
961 // 11. tentative(sym) and undf(global_sym) => sym
962 // 12. undf(sym) and * => global_sym
963 //
964 // Reduces to:
965 // 1. strong(sym) and strong(global_sym) => error
966 // 2. * and strong(global_sym) => global_sym
967 // 3. weak(sym) and weak(global_sym) => global_sym
968 // 4. tentative(sym) and tentative(global_sym) => pick larger
969 // 5. undf(sym) and * => global_sym
970 // 6. else => sym
971
972 const sym_is_strong = sym.sect() and !(sym.weakDef() or sym.pext());
973 const global_is_strong = global_sym.sect() and !(global_sym.weakDef() or global_sym.pext());
974 const sym_is_weak = sym.sect() and (sym.weakDef() or sym.pext());
975 const global_is_weak = global_sym.sect() and (global_sym.weakDef() or global_sym.pext());
976
977 if (sym_is_strong and global_is_strong) {
978 log.err("symbol '{s}' defined multiple times", .{sym_name});
979 if (global.getFile()) |file| {
980 log.err(" first definition in '{s}'", .{self.objects.items[file].name});
981 }
982 log.err(" next definition in '{s}'", .{self.objects.items[object_id].name});
983 return error.MultipleSymbolDefinitions;
984 }
985
986 const update_global = blk: {
987 if (global_is_strong) break :blk false;
988 if (sym_is_weak and global_is_weak) break :blk false;
989 if (sym.tentative() and global_sym.tentative()) {
990 if (global_sym.n_value >= sym.n_value) break :blk false;
991 }
992 if (sym.undf() and !sym.tentative()) break :blk false;
993 break :blk true;
994 };
995
996 if (update_global) {
997 if (global.getFile()) |file| {
998 const global_object = &self.objects.items[file];
999 global_object.globals_lookup[global.sym_index] = global_index;
1000 }
1001 _ = resolver.unresolved.swapRemove(resolver.table.get(sym_name).?);
1002 global.* = sym_loc;
1003 } else {
1004 object.globals_lookup[sym_index] = global_index;
1005 }
1006 }
1007 }
1008
1009 fn resolveSymbolsInArchives(self: *Zld, resolver: *SymbolResolver) !void {
1010 if (self.archives.items.len == 0) return;
1011
1012 const gpa = self.gpa;
1013 const cpu_arch = self.options.target.cpu.arch;
1014 var next_sym: usize = 0;
1015 loop: while (next_sym < resolver.unresolved.count()) {
1016 const global = self.globals.items[resolver.unresolved.keys()[next_sym]];
1017 const sym_name = self.getSymbolName(global);
1018
1019 for (self.archives.items) |archive| {
1020 // Check if the entry exists in a static archive.
1021 const offsets = archive.toc.get(sym_name) orelse {
1022 // No hit.
1023 continue;
1024 };
1025 assert(offsets.items.len > 0);
1026
1027 const object_id = @as(u16, @intCast(self.objects.items.len));
1028 const object = archive.parseObject(gpa, cpu_arch, offsets.items[0]) catch |e| switch (e) {
1029 error.MismatchedCpuArchitecture => {
1030 log.err("CPU architecture mismatch found in {s}", .{archive.name});
1031 return e;
1032 },
1033 else => return e,
1034 };
1035 try self.objects.append(gpa, object);
1036 try self.resolveSymbolsInObject(object_id, resolver);
1037
1038 continue :loop;
1039 }
1040
1041 next_sym += 1;
1042 }
1043 }
1044
1045 fn resolveSymbolsInDylibs(self: *Zld, resolver: *SymbolResolver) !void {
1046 if (self.dylibs.items.len == 0) return;
1047
1048 var next_sym: usize = 0;
1049 loop: while (next_sym < resolver.unresolved.count()) {
1050 const global_index = resolver.unresolved.keys()[next_sym];
1051 const global = self.globals.items[global_index];
1052 const sym = self.getSymbolPtr(global);
1053 const sym_name = self.getSymbolName(global);
1054
1055 for (self.dylibs.items, 0..) |dylib, id| {
1056 if (!dylib.symbols.contains(sym_name)) continue;
1057
1058 const dylib_id = @as(u16, @intCast(id));
1059 if (!self.referenced_dylibs.contains(dylib_id)) {
1060 try self.referenced_dylibs.putNoClobber(self.gpa, dylib_id, {});
1061 }
1062
1063 const ordinal = self.referenced_dylibs.getIndex(dylib_id) orelse unreachable;
1064 sym.n_type |= macho.N_EXT;
1065 sym.n_desc = @as(u16, @intCast(ordinal + 1)) * macho.N_SYMBOL_RESOLVER;
1066
1067 if (dylib.weak) {
1068 sym.n_desc |= macho.N_WEAK_REF;
1069 }
1070
1071 assert(resolver.unresolved.swapRemove(global_index));
1072 continue :loop;
1073 }
1074
1075 next_sym += 1;
1076 }
1077 }
1078
1079 fn resolveSymbolsAtLoading(self: *Zld, resolver: *SymbolResolver) !void {
1080 const is_lib = self.options.output_mode == .Lib;
1081 const is_dyn_lib = self.options.link_mode == .Dynamic and is_lib;
1082 const allow_undef = is_dyn_lib and (self.options.allow_shlib_undefined orelse false);
1083
1084 var next_sym: usize = 0;
1085 while (next_sym < resolver.unresolved.count()) {
1086 const global_index = resolver.unresolved.keys()[next_sym];
1087 const global = self.globals.items[global_index];
1088 const sym = self.getSymbolPtr(global);
1089 const sym_name = self.getSymbolName(global);
1090
1091 if (sym.discarded()) {
1092 sym.* = .{
1093 .n_strx = 0,
1094 .n_type = macho.N_UNDF,
1095 .n_sect = 0,
1096 .n_desc = 0,
1097 .n_value = 0,
1098 };
1099 _ = resolver.unresolved.swapRemove(global_index);
1100 continue;
1101 } else if (allow_undef) {
1102 const n_desc = @as(
1103 u16,
1104 @bitCast(macho.BIND_SPECIAL_DYLIB_FLAT_LOOKUP * @as(i16, @intCast(macho.N_SYMBOL_RESOLVER))),
1105 );
1106 sym.n_type = macho.N_EXT;
1107 sym.n_desc = n_desc;
1108 _ = resolver.unresolved.swapRemove(global_index);
1109 continue;
1110 }
1111
1112 log.err("undefined reference to symbol '{s}'", .{sym_name});
1113 if (global.getFile()) |file| {
1114 log.err(" first referenced in '{s}'", .{self.objects.items[file].name});
1115 }
1116
1117 next_sym += 1;
1118 }
1119 }
1120
1121 fn createMhExecuteHeaderSymbol(self: *Zld, resolver: *SymbolResolver) !void {
1122 if (self.options.output_mode != .Exe) return;
1123 if (resolver.table.get("__mh_execute_header")) |global_index| {
1124 const global = self.globals.items[global_index];
1125 const sym = self.getSymbol(global);
1126 self.mh_execute_header_index = global_index;
1127 if (!sym.undf() and !(sym.pext() or sym.weakDef())) return;
1128 }
1129
1130 const gpa = self.gpa;
1131 const sym_index = try self.allocateSymbol();
1132 const sym_loc = SymbolWithLoc{ .sym_index = sym_index };
1133 const sym = self.getSymbolPtr(sym_loc);
1134 sym.n_strx = try self.strtab.insert(gpa, "__mh_execute_header");
1135 sym.n_type = macho.N_SECT | macho.N_EXT;
1136 sym.n_desc = macho.REFERENCED_DYNAMICALLY;
1137
1138 if (resolver.table.get("__mh_execute_header")) |global_index| {
1139 const global = &self.globals.items[global_index];
1140 const global_object = &self.objects.items[global.getFile().?];
1141 global_object.globals_lookup[global.sym_index] = global_index;
1142 global.* = sym_loc;
1143 self.mh_execute_header_index = global_index;
1144 } else {
1145 self.mh_execute_header_index = try self.addGlobal(sym_loc);
1146 }
1147 }
1148
1149 fn createDsoHandleSymbol(self: *Zld, resolver: *SymbolResolver) !void {
1150 const global_index = resolver.table.get("___dso_handle") orelse return;
1151 const global = &self.globals.items[global_index];
1152 self.dso_handle_index = global_index;
1153 if (!self.getSymbol(global.*).undf()) return;
1154
1155 const gpa = self.gpa;
1156 const sym_index = try self.allocateSymbol();
1157 const sym_loc = SymbolWithLoc{ .sym_index = sym_index };
1158 const sym = self.getSymbolPtr(sym_loc);
1159 sym.n_strx = try self.strtab.insert(gpa, "___dso_handle");
1160 sym.n_type = macho.N_SECT | macho.N_EXT;
1161 sym.n_desc = macho.N_WEAK_DEF;
1162
1163 const global_object = &self.objects.items[global.getFile().?];
1164 global_object.globals_lookup[global.sym_index] = global_index;
1165 _ = resolver.unresolved.swapRemove(resolver.table.get("___dso_handle").?);
1166 global.* = sym_loc;
1167 }
1168
1169 pub fn deinit(self: *Zld) void {
1170 const gpa = self.gpa;
1171
1172 self.tlv_ptr_entries.deinit(gpa);
1173 self.tlv_ptr_table.deinit(gpa);
1174 self.got_entries.deinit(gpa);
1175 self.got_table.deinit(gpa);
1176 self.stubs.deinit(gpa);
1177 self.stubs_table.deinit(gpa);
1178 self.thunk_table.deinit(gpa);
1179
1180 for (self.thunks.items) |*thunk| {
1181 thunk.deinit(gpa);
1182 }
1183 self.thunks.deinit(gpa);
1184
1185 self.strtab.deinit(gpa);
1186 self.locals.deinit(gpa);
1187 self.globals.deinit(gpa);
1188
1189 for (self.objects.items) |*object| {
1190 object.deinit(gpa);
1191 }
1192 self.objects.deinit(gpa);
1193 for (self.archives.items) |*archive| {
1194 archive.deinit(gpa);
1195 }
1196 self.archives.deinit(gpa);
1197 for (self.dylibs.items) |*dylib| {
1198 dylib.deinit(gpa);
1199 }
1200 self.dylibs.deinit(gpa);
1201 self.dylibs_map.deinit(gpa);
1202 self.referenced_dylibs.deinit(gpa);
1203
1204 self.segments.deinit(gpa);
1205 self.sections.deinit(gpa);
1206 self.atoms.deinit(gpa);
1207 }
1208
1209 fn createSegments(self: *Zld) !void {
1210 const pagezero_vmsize = self.options.pagezero_size orelse MachO.default_pagezero_vmsize;
1211 const aligned_pagezero_vmsize = mem.alignBackward(u64, pagezero_vmsize, self.page_size);
1212 if (self.options.output_mode != .Lib and aligned_pagezero_vmsize > 0) {
1213 if (aligned_pagezero_vmsize != pagezero_vmsize) {
1214 log.warn("requested __PAGEZERO size (0x{x}) is not page aligned", .{pagezero_vmsize});
1215 log.warn(" rounding down to 0x{x}", .{aligned_pagezero_vmsize});
1216 }
1217 try self.segments.append(self.gpa, .{
1218 .cmdsize = @sizeOf(macho.segment_command_64),
1219 .segname = makeStaticString("__PAGEZERO"),
1220 .vmsize = aligned_pagezero_vmsize,
1221 });
1222 }
1223
1224 // __TEXT segment is non-optional
1225 {
1226 const protection = getSegmentMemoryProtection("__TEXT");
1227 try self.segments.append(self.gpa, .{
1228 .cmdsize = @sizeOf(macho.segment_command_64),
1229 .segname = makeStaticString("__TEXT"),
1230 .maxprot = protection,
1231 .initprot = protection,
1232 });
1233 }
1234
1235 for (self.sections.items(.header), 0..) |header, sect_id| {
1236 if (header.size == 0) continue; // empty section
1237
1238 const segname = header.segName();
1239 const segment_id = self.getSegmentByName(segname) orelse blk: {
1240 log.debug("creating segment '{s}'", .{segname});
1241 const segment_id = @as(u8, @intCast(self.segments.items.len));
1242 const protection = getSegmentMemoryProtection(segname);
1243 try self.segments.append(self.gpa, .{
1244 .cmdsize = @sizeOf(macho.segment_command_64),
1245 .segname = makeStaticString(segname),
1246 .maxprot = protection,
1247 .initprot = protection,
1248 });
1249 break :blk segment_id;
1250 };
1251 const segment = &self.segments.items[segment_id];
1252 segment.cmdsize += @sizeOf(macho.section_64);
1253 segment.nsects += 1;
1254 self.sections.items(.segment_index)[sect_id] = segment_id;
1255 }
1256
1257 // __LINKEDIT always comes last
1258 {
1259 const protection = getSegmentMemoryProtection("__LINKEDIT");
1260 try self.segments.append(self.gpa, .{
1261 .cmdsize = @sizeOf(macho.segment_command_64),
1262 .segname = makeStaticString("__LINKEDIT"),
1263 .maxprot = protection,
1264 .initprot = protection,
1265 });
1266 }
1267 }
1268
1269 pub fn allocateSymbol(self: *Zld) !u32 {
1270 try self.locals.ensureUnusedCapacity(self.gpa, 1);
1271 log.debug(" (allocating symbol index {d})", .{self.locals.items.len});
1272 const index = @as(u32, @intCast(self.locals.items.len));
1273 _ = self.locals.addOneAssumeCapacity();
1274 self.locals.items[index] = .{
1275 .n_strx = 0,
1276 .n_type = 0,
1277 .n_sect = 0,
1278 .n_desc = 0,
1279 .n_value = 0,
1280 };
1281 return index;
1282 }
1283
1284 fn addGlobal(self: *Zld, sym_loc: SymbolWithLoc) !u32 {
1285 const global_index = @as(u32, @intCast(self.globals.items.len));
1286 try self.globals.append(self.gpa, sym_loc);
1287 return global_index;
1288 }
1289
1290 fn allocateSpecialSymbols(self: *Zld) !void {
1291 for (&[_]?u32{
1292 self.dso_handle_index,
1293 self.mh_execute_header_index,
1294 }) |maybe_index| {
1295 const global_index = maybe_index orelse continue;
1296 const global = self.globals.items[global_index];
1297 if (global.getFile() != null) continue;
1298 const name = self.getSymbolName(global);
1299 const sym = self.getSymbolPtr(global);
1300 const segment_index = self.getSegmentByName("__TEXT").?;
1301 const seg = self.segments.items[segment_index];
1302 sym.n_sect = 1;
1303 sym.n_value = seg.vmaddr;
1304 log.debug("allocating {s} at the start of {s}", .{
1305 name,
1306 seg.segName(),
1307 });
1308 }
1309 }
1310
1311 fn writeAtoms(self: *Zld) !void {
1312 const gpa = self.gpa;
1313 const slice = self.sections.slice();
1314
1315 for (slice.items(.first_atom_index), 0..) |first_atom_index, sect_id| {
1316 const header = slice.items(.header)[sect_id];
1317 var atom_index = first_atom_index;
1318
1319 if (atom_index == 0) continue;
1320 if (header.isZerofill()) continue;
1321
1322 var buffer = std.ArrayList(u8).init(gpa);
1323 defer buffer.deinit();
1324 try buffer.ensureTotalCapacity(math.cast(usize, header.size) orelse return error.Overflow);
1325
1326 log.debug("writing atoms in {s},{s}", .{ header.segName(), header.sectName() });
1327
1328 var count: u32 = 0;
1329 while (true) : (count += 1) {
1330 const atom = self.getAtom(atom_index);
1331 const this_sym = self.getSymbol(atom.getSymbolWithLoc());
1332 const padding_size: usize = if (atom.next_index) |next_index| blk: {
1333 const next_sym = self.getSymbol(self.getAtom(next_index).getSymbolWithLoc());
1334 const size = next_sym.n_value - (this_sym.n_value + atom.size);
1335 break :blk math.cast(usize, size) orelse return error.Overflow;
1336 } else 0;
1337
1338 log.debug(" (adding ATOM(%{d}, '{s}') from object({?}) to buffer)", .{
1339 atom.sym_index,
1340 self.getSymbolName(atom.getSymbolWithLoc()),
1341 atom.getFile(),
1342 });
1343 if (padding_size > 0) {
1344 log.debug(" (with padding {x})", .{padding_size});
1345 }
1346
1347 const offset = buffer.items.len;
1348
1349 // TODO: move writing synthetic sections into a separate function
1350 if (atom.getFile() == null) outer: {
1351 if (self.dyld_private_sym_index) |sym_index| {
1352 if (atom.sym_index == sym_index) {
1353 buffer.appendSliceAssumeCapacity(&[_]u8{0} ** @sizeOf(u64));
1354 break :outer;
1355 }
1356 }
1357 switch (header.type()) {
1358 macho.S_NON_LAZY_SYMBOL_POINTERS => {
1359 try self.writeGotPointer(count, buffer.writer());
1360 },
1361 macho.S_LAZY_SYMBOL_POINTERS => {
1362 try self.writeLazyPointer(count, buffer.writer());
1363 },
1364 macho.S_THREAD_LOCAL_VARIABLE_POINTERS => {
1365 buffer.appendSliceAssumeCapacity(&[_]u8{0} ** @sizeOf(u64));
1366 },
1367 else => {
1368 if (self.stub_helper_preamble_sym_index) |sym_index| {
1369 if (sym_index == atom.sym_index) {
1370 try self.writeStubHelperPreambleCode(buffer.writer());
1371 break :outer;
1372 }
1373 }
1374 if (header.type() == macho.S_SYMBOL_STUBS) {
1375 try self.writeStubCode(atom_index, count, buffer.writer());
1376 } else if (mem.eql(u8, header.sectName(), "__stub_helper")) {
1377 try self.writeStubHelperCode(atom_index, buffer.writer());
1378 } else if (header.isCode()) {
1379 // A thunk
1380 try thunks.writeThunkCode(self, atom_index, buffer.writer());
1381 } else unreachable;
1382 },
1383 }
1384 } else {
1385 const code = Atom.getAtomCode(self, atom_index);
1386 const relocs = Atom.getAtomRelocs(self, atom_index);
1387 const size = math.cast(usize, atom.size) orelse return error.Overflow;
1388 buffer.appendSliceAssumeCapacity(code);
1389 try Atom.resolveRelocs(
1390 self,
1391 atom_index,
1392 buffer.items[offset..][0..size],
1393 relocs,
1394 );
1395 }
1396
1397 var i: usize = 0;
1398 while (i < padding_size) : (i += 1) {
1399 // TODO with NOPs
1400 buffer.appendAssumeCapacity(0);
1401 }
1402
1403 if (atom.next_index) |next_index| {
1404 atom_index = next_index;
1405 } else {
1406 assert(buffer.items.len == header.size);
1407 log.debug(" (writing at file offset 0x{x})", .{header.offset});
1408 try self.file.pwriteAll(buffer.items, header.offset);
1409 break;
1410 }
1411 }
1412 }
1413 }
1414
1415 fn pruneAndSortSections(self: *Zld) !void {
1416 const gpa = self.gpa;
1417
1418 const SortSection = struct {
1419 pub fn lessThan(_: void, lhs: Section, rhs: Section) bool {
1420 return getSectionPrecedence(lhs.header) < getSectionPrecedence(rhs.header);
1421 }
1422 };
1423
1424 const slice = self.sections.slice();
1425 var sections = std.ArrayList(Section).init(gpa);
1426 defer sections.deinit();
1427 try sections.ensureTotalCapacity(slice.len);
1428
1429 {
1430 var i: u8 = 0;
1431 while (i < slice.len) : (i += 1) {
1432 const section = self.sections.get(i);
1433 if (section.header.size == 0) {
1434 log.debug("pruning section {s},{s} {d}", .{
1435 section.header.segName(),
1436 section.header.sectName(),
1437 section.first_atom_index,
1438 });
1439 continue;
1440 }
1441 sections.appendAssumeCapacity(section);
1442 }
1443 }
1444
1445 mem.sort(Section, sections.items, {}, SortSection.lessThan);
1446
1447 self.sections.shrinkRetainingCapacity(0);
1448 for (sections.items) |out| {
1449 self.sections.appendAssumeCapacity(out);
1450 }
1451 }
1452
1453 fn calcSectionSizes(self: *Zld) !void {
1454 const slice = self.sections.slice();
1455 for (slice.items(.header), 0..) |*header, sect_id| {
1456 if (header.size == 0) continue;
1457 if (self.requiresThunks()) {
1458 if (header.isCode() and !(header.type() == macho.S_SYMBOL_STUBS) and !mem.eql(u8, header.sectName(), "__stub_helper")) continue;
1459 }
1460
1461 var atom_index = slice.items(.first_atom_index)[sect_id];
1462 if (atom_index == 0) continue;
1463
1464 header.size = 0;
1465 header.@"align" = 0;
1466
1467 while (true) {
1468 const atom = self.getAtom(atom_index);
1469 const atom_alignment = try math.powi(u32, 2, atom.alignment);
1470 const atom_offset = mem.alignForward(u64, header.size, atom_alignment);
1471 const padding = atom_offset - header.size;
1472
1473 const sym = self.getSymbolPtr(atom.getSymbolWithLoc());
1474 sym.n_value = atom_offset;
1475
1476 header.size += padding + atom.size;
1477 header.@"align" = @max(header.@"align", atom.alignment);
1478
1479 if (atom.next_index) |next_index| {
1480 atom_index = next_index;
1481 } else break;
1482 }
1483 }
1484
1485 if (self.requiresThunks()) {
1486 for (slice.items(.header), 0..) |header, sect_id| {
1487 if (!header.isCode()) continue;
1488 if (header.type() == macho.S_SYMBOL_STUBS) continue;
1489 if (mem.eql(u8, header.sectName(), "__stub_helper")) continue;
1490
1491 // Create jump/branch range extenders if needed.
1492 try thunks.createThunks(self, @as(u8, @intCast(sect_id)));
1493 }
1494 }
1495
1496 // Update offsets of all symbols contained within each Atom.
1497 // We need to do this since our unwind info synthesiser relies on
1498 // traversing the symbols when synthesising unwind info and DWARF CFI records.
1499 for (slice.items(.first_atom_index)) |first_atom_index| {
1500 if (first_atom_index == 0) continue;
1501 var atom_index = first_atom_index;
1502
1503 while (true) {
1504 const atom = self.getAtom(atom_index);
1505 const sym = self.getSymbol(atom.getSymbolWithLoc());
1506
1507 if (atom.getFile() != null) {
1508 // Update each symbol contained within the atom
1509 var it = Atom.getInnerSymbolsIterator(self, atom_index);
1510 while (it.next()) |sym_loc| {
1511 const inner_sym = self.getSymbolPtr(sym_loc);
1512 inner_sym.n_value = sym.n_value + Atom.calcInnerSymbolOffset(
1513 self,
1514 atom_index,
1515 sym_loc.sym_index,
1516 );
1517 }
1518
1519 // If there is a section alias, update it now too
1520 if (Atom.getSectionAlias(self, atom_index)) |sym_loc| {
1521 const alias = self.getSymbolPtr(sym_loc);
1522 alias.n_value = sym.n_value;
1523 }
1524 }
1525
1526 if (atom.next_index) |next_index| {
1527 atom_index = next_index;
1528 } else break;
1529 }
1530 }
1531 }
1532
1533 fn allocateSegments(self: *Zld) !void {
1534 for (self.segments.items, 0..) |*segment, segment_index| {
1535 const is_text_segment = mem.eql(u8, segment.segName(), "__TEXT");
1536 const base_size = if (is_text_segment) try load_commands.calcMinHeaderPad(self.gpa, self.options, .{
1537 .segments = self.segments.items,
1538 .dylibs = self.dylibs.items,
1539 .referenced_dylibs = self.referenced_dylibs.keys(),
1540 }) else 0;
1541 try self.allocateSegment(@as(u8, @intCast(segment_index)), base_size);
1542 }
1543 }
1544
1545 fn getSegmentAllocBase(self: Zld, segment_index: u8) struct { vmaddr: u64, fileoff: u64 } {
1546 if (segment_index > 0) {
1547 const prev_segment = self.segments.items[segment_index - 1];
1548 return .{
1549 .vmaddr = prev_segment.vmaddr + prev_segment.vmsize,
1550 .fileoff = prev_segment.fileoff + prev_segment.filesize,
1551 };
1552 }
1553 return .{ .vmaddr = 0, .fileoff = 0 };
1554 }
1555
1556 fn allocateSegment(self: *Zld, segment_index: u8, init_size: u64) !void {
1557 const segment = &self.segments.items[segment_index];
1558
1559 if (mem.eql(u8, segment.segName(), "__PAGEZERO")) return; // allocated upon creation
1560
1561 const base = self.getSegmentAllocBase(segment_index);
1562 segment.vmaddr = base.vmaddr;
1563 segment.fileoff = base.fileoff;
1564 segment.filesize = init_size;
1565 segment.vmsize = init_size;
1566
1567 // Allocate the sections according to their alignment at the beginning of the segment.
1568 const indexes = self.getSectionIndexes(segment_index);
1569 var start = init_size;
1570
1571 const slice = self.sections.slice();
1572 for (slice.items(.header)[indexes.start..indexes.end], 0..) |*header, sect_id| {
1573 const alignment = try math.powi(u32, 2, header.@"align");
1574 const start_aligned = mem.alignForward(u64, start, alignment);
1575 const n_sect = @as(u8, @intCast(indexes.start + sect_id + 1));
1576
1577 header.offset = if (header.isZerofill())
1578 0
1579 else
1580 @as(u32, @intCast(segment.fileoff + start_aligned));
1581 header.addr = segment.vmaddr + start_aligned;
1582
1583 var atom_index = slice.items(.first_atom_index)[indexes.start + sect_id];
1584 if (atom_index > 0) {
1585 log.debug("allocating local symbols in sect({d}, '{s},{s}')", .{
1586 n_sect,
1587 header.segName(),
1588 header.sectName(),
1589 });
1590
1591 while (true) {
1592 const atom = self.getAtom(atom_index);
1593 const sym = self.getSymbolPtr(atom.getSymbolWithLoc());
1594 sym.n_value += header.addr;
1595 sym.n_sect = n_sect;
1596
1597 log.debug(" ATOM(%{d}, '{s}') @{x}", .{
1598 atom.sym_index,
1599 self.getSymbolName(atom.getSymbolWithLoc()),
1600 sym.n_value,
1601 });
1602
1603 if (atom.getFile() != null) {
1604 // Update each symbol contained within the atom
1605 var it = Atom.getInnerSymbolsIterator(self, atom_index);
1606 while (it.next()) |sym_loc| {
1607 const inner_sym = self.getSymbolPtr(sym_loc);
1608 inner_sym.n_value = sym.n_value + Atom.calcInnerSymbolOffset(
1609 self,
1610 atom_index,
1611 sym_loc.sym_index,
1612 );
1613 inner_sym.n_sect = n_sect;
1614 }
1615
1616 // If there is a section alias, update it now too
1617 if (Atom.getSectionAlias(self, atom_index)) |sym_loc| {
1618 const alias = self.getSymbolPtr(sym_loc);
1619 alias.n_value = sym.n_value;
1620 alias.n_sect = n_sect;
1621 }
1622 }
1623
1624 if (atom.next_index) |next_index| {
1625 atom_index = next_index;
1626 } else break;
1627 }
1628 }
1629
1630 start = start_aligned + header.size;
1631
1632 if (!header.isZerofill()) {
1633 segment.filesize = start;
1634 }
1635 segment.vmsize = start;
1636 }
1637
1638 segment.filesize = mem.alignForward(u64, segment.filesize, self.page_size);
1639 segment.vmsize = mem.alignForward(u64, segment.vmsize, self.page_size);
1640 }
1641
1642 const InitSectionOpts = struct {
1643 flags: u32 = macho.S_REGULAR,
1644 reserved1: u32 = 0,
1645 reserved2: u32 = 0,
1646 };
1647
1648 pub fn initSection(
1649 self: *Zld,
1650 segname: []const u8,
1651 sectname: []const u8,
1652 opts: InitSectionOpts,
1653 ) !u8 {
1654 const gpa = self.gpa;
1655 log.debug("creating section '{s},{s}'", .{ segname, sectname });
1656 const index = @as(u8, @intCast(self.sections.slice().len));
1657 try self.sections.append(gpa, .{
1658 .segment_index = undefined, // Segments will be created automatically later down the pipeline
1659 .header = .{
1660 .sectname = makeStaticString(sectname),
1661 .segname = makeStaticString(segname),
1662 .flags = opts.flags,
1663 .reserved1 = opts.reserved1,
1664 .reserved2 = opts.reserved2,
1665 },
1666 .first_atom_index = 0,
1667 .last_atom_index = 0,
1668 });
1669 return index;
1670 }
1671
1672 fn getSegmentPrecedence(segname: []const u8) u4 {
1673 if (mem.eql(u8, segname, "__PAGEZERO")) return 0x0;
1674 if (mem.eql(u8, segname, "__TEXT")) return 0x1;
1675 if (mem.eql(u8, segname, "__DATA_CONST")) return 0x2;
1676 if (mem.eql(u8, segname, "__DATA")) return 0x3;
1677 if (mem.eql(u8, segname, "__LINKEDIT")) return 0x5;
1678 return 0x4;
1679 }
1680
1681 fn getSegmentMemoryProtection(segname: []const u8) macho.vm_prot_t {
1682 if (mem.eql(u8, segname, "__PAGEZERO")) return macho.PROT.NONE;
1683 if (mem.eql(u8, segname, "__TEXT")) return macho.PROT.READ | macho.PROT.EXEC;
1684 if (mem.eql(u8, segname, "__LINKEDIT")) return macho.PROT.READ;
1685 return macho.PROT.READ | macho.PROT.WRITE;
1686 }
1687
1688 fn getSectionPrecedence(header: macho.section_64) u8 {
1689 const segment_precedence: u4 = getSegmentPrecedence(header.segName());
1690 const section_precedence: u4 = blk: {
1691 if (header.isCode()) {
1692 if (mem.eql(u8, "__text", header.sectName())) break :blk 0x0;
1693 if (header.type() == macho.S_SYMBOL_STUBS) break :blk 0x1;
1694 break :blk 0x2;
1695 }
1696 switch (header.type()) {
1697 macho.S_NON_LAZY_SYMBOL_POINTERS,
1698 macho.S_LAZY_SYMBOL_POINTERS,
1699 => break :blk 0x0,
1700 macho.S_MOD_INIT_FUNC_POINTERS => break :blk 0x1,
1701 macho.S_MOD_TERM_FUNC_POINTERS => break :blk 0x2,
1702 macho.S_ZEROFILL => break :blk 0xf,
1703 macho.S_THREAD_LOCAL_REGULAR => break :blk 0xd,
1704 macho.S_THREAD_LOCAL_ZEROFILL => break :blk 0xe,
1705 else => {
1706 if (mem.eql(u8, "__unwind_info", header.sectName())) break :blk 0xe;
1707 if (mem.eql(u8, "__eh_frame", header.sectName())) break :blk 0xf;
1708 break :blk 0x3;
1709 },
1710 }
1711 };
1712 return (@as(u8, @intCast(segment_precedence)) << 4) + section_precedence;
1713 }
1714
1715 fn writeSegmentHeaders(self: *Zld, writer: anytype) !void {
1716 for (self.segments.items, 0..) |seg, i| {
1717 const indexes = self.getSectionIndexes(@as(u8, @intCast(i)));
1718 var out_seg = seg;
1719 out_seg.cmdsize = @sizeOf(macho.segment_command_64);
1720 out_seg.nsects = 0;
1721
1722 // Update section headers count; any section with size of 0 is excluded
1723 // since it doesn't have any data in the final binary file.
1724 for (self.sections.items(.header)[indexes.start..indexes.end]) |header| {
1725 if (header.size == 0) continue;
1726 out_seg.cmdsize += @sizeOf(macho.section_64);
1727 out_seg.nsects += 1;
1728 }
1729
1730 if (out_seg.nsects == 0 and
1731 (mem.eql(u8, out_seg.segName(), "__DATA_CONST") or
1732 mem.eql(u8, out_seg.segName(), "__DATA"))) continue;
1733
1734 try writer.writeStruct(out_seg);
1735 for (self.sections.items(.header)[indexes.start..indexes.end]) |header| {
1736 if (header.size == 0) continue;
1737 try writer.writeStruct(header);
1738 }
1739 }
1740 }
1741
1742 fn writeLinkeditSegmentData(self: *Zld) !void {
1743 try self.writeDyldInfoData();
1744 try self.writeFunctionStarts();
1745 try self.writeDataInCode();
1746 try self.writeSymtabs();
1747
1748 const seg = self.getLinkeditSegmentPtr();
1749 seg.vmsize = mem.alignForward(u64, seg.filesize, self.page_size);
1750 }
1751
1752 fn collectRebaseDataFromContainer(
1753 self: *Zld,
1754 sect_id: u8,
1755 rebase: *Rebase,
1756 container: anytype,
1757 ) !void {
1758 const slice = self.sections.slice();
1759 const segment_index = slice.items(.segment_index)[sect_id];
1760 const seg = self.getSegment(sect_id);
1761
1762 try rebase.entries.ensureUnusedCapacity(self.gpa, container.items.len);
1763
1764 for (container.items) |entry| {
1765 const target_sym = entry.getTargetSymbol(self);
1766 if (target_sym.undf()) continue;
1767
1768 const atom_sym = entry.getAtomSymbol(self);
1769 const base_offset = atom_sym.n_value - seg.vmaddr;
1770
1771 log.debug(" | rebase at {x}", .{base_offset});
1772
1773 rebase.entries.appendAssumeCapacity(.{
1774 .offset = base_offset,
1775 .segment_id = segment_index,
1776 });
1777 }
1778 }
1779
1780 fn collectRebaseData(self: *Zld, rebase: *Rebase) !void {
1781 log.debug("collecting rebase data", .{});
1782
1783 // First, unpack GOT entries
1784 if (self.getSectionByName("__DATA_CONST", "__got")) |sect_id| {
1785 try self.collectRebaseDataFromContainer(sect_id, rebase, self.got_entries);
1786 }
1787
1788 const slice = self.sections.slice();
1789
1790 // Next, unpact lazy pointers
1791 // TODO: save la_ptr in a container so that we can re-use the helper
1792 if (self.getSectionByName("__DATA", "__la_symbol_ptr")) |sect_id| {
1793 const segment_index = slice.items(.segment_index)[sect_id];
1794 const seg = self.getSegment(sect_id);
1795 var atom_index = slice.items(.first_atom_index)[sect_id];
1796
1797 try rebase.entries.ensureUnusedCapacity(self.gpa, self.stubs.items.len);
1798
1799 while (true) {
1800 const atom = self.getAtom(atom_index);
1801 const sym = self.getSymbol(atom.getSymbolWithLoc());
1802 const base_offset = sym.n_value - seg.vmaddr;
1803
1804 log.debug(" | rebase at {x}", .{base_offset});
1805
1806 rebase.entries.appendAssumeCapacity(.{
1807 .offset = base_offset,
1808 .segment_id = segment_index,
1809 });
1810
1811 if (atom.next_index) |next_index| {
1812 atom_index = next_index;
1813 } else break;
1814 }
1815 }
1816
1817 // Finally, unpack the rest.
1818 for (slice.items(.header), 0..) |header, sect_id| {
1819 switch (header.type()) {
1820 macho.S_LITERAL_POINTERS,
1821 macho.S_REGULAR,
1822 macho.S_MOD_INIT_FUNC_POINTERS,
1823 macho.S_MOD_TERM_FUNC_POINTERS,
1824 => {},
1825 else => continue,
1826 }
1827
1828 const segment_index = slice.items(.segment_index)[sect_id];
1829 const segment = self.getSegment(@as(u8, @intCast(sect_id)));
1830 if (segment.maxprot & macho.PROT.WRITE == 0) continue;
1831
1832 log.debug("{s},{s}", .{ header.segName(), header.sectName() });
1833
1834 const cpu_arch = self.options.target.cpu.arch;
1835 var atom_index = slice.items(.first_atom_index)[sect_id];
1836 if (atom_index == 0) continue;
1837
1838 while (true) {
1839 const atom = self.getAtom(atom_index);
1840 const sym = self.getSymbol(atom.getSymbolWithLoc());
1841
1842 const should_rebase = blk: {
1843 if (self.dyld_private_sym_index) |sym_index| {
1844 if (atom.getFile() == null and atom.sym_index == sym_index) break :blk false;
1845 }
1846 break :blk !sym.undf();
1847 };
1848
1849 if (should_rebase) {
1850 log.debug(" ATOM({d}, %{d}, '{s}')", .{ atom_index, atom.sym_index, self.getSymbolName(atom.getSymbolWithLoc()) });
1851
1852 const code = Atom.getAtomCode(self, atom_index);
1853 const relocs = Atom.getAtomRelocs(self, atom_index);
1854 const ctx = Atom.getRelocContext(self, atom_index);
1855
1856 for (relocs) |rel| {
1857 switch (cpu_arch) {
1858 .aarch64 => {
1859 const rel_type = @as(macho.reloc_type_arm64, @enumFromInt(rel.r_type));
1860 if (rel_type != .ARM64_RELOC_UNSIGNED) continue;
1861 if (rel.r_length != 3) continue;
1862 },
1863 .x86_64 => {
1864 const rel_type = @as(macho.reloc_type_x86_64, @enumFromInt(rel.r_type));
1865 if (rel_type != .X86_64_RELOC_UNSIGNED) continue;
1866 if (rel.r_length != 3) continue;
1867 },
1868 else => unreachable,
1869 }
1870 const target = Atom.parseRelocTarget(self, .{
1871 .object_id = atom.getFile().?,
1872 .rel = rel,
1873 .code = code,
1874 .base_offset = ctx.base_offset,
1875 .base_addr = ctx.base_addr,
1876 });
1877 const target_sym = self.getSymbol(target);
1878 if (target_sym.undf()) continue;
1879
1880 const base_offset = @as(i32, @intCast(sym.n_value - segment.vmaddr));
1881 const rel_offset = rel.r_address - ctx.base_offset;
1882 const offset = @as(u64, @intCast(base_offset + rel_offset));
1883 log.debug(" | rebase at {x}", .{offset});
1884
1885 try rebase.entries.append(self.gpa, .{
1886 .offset = offset,
1887 .segment_id = segment_index,
1888 });
1889 }
1890 }
1891
1892 if (atom.next_index) |next_index| {
1893 atom_index = next_index;
1894 } else break;
1895 }
1896 }
1897
1898 try rebase.finalize(self.gpa);
1899 }
1900
1901 fn collectBindDataFromContainer(
1902 self: *Zld,
1903 sect_id: u8,
1904 bind: *Bind,
1905 container: anytype,
1906 ) !void {
1907 const slice = self.sections.slice();
1908 const segment_index = slice.items(.segment_index)[sect_id];
1909 const seg = self.getSegment(sect_id);
1910
1911 try bind.entries.ensureUnusedCapacity(self.gpa, container.items.len);
1912
1913 for (container.items) |entry| {
1914 const bind_sym_name = entry.getTargetSymbolName(self);
1915 const bind_sym = entry.getTargetSymbol(self);
1916 if (bind_sym.sect()) continue;
1917
1918 const sym = entry.getAtomSymbol(self);
1919 const base_offset = sym.n_value - seg.vmaddr;
1920
1921 const dylib_ordinal = @divTrunc(@as(i16, @bitCast(bind_sym.n_desc)), macho.N_SYMBOL_RESOLVER);
1922 log.debug(" | bind at {x}, import('{s}') in dylib({d})", .{
1923 base_offset,
1924 bind_sym_name,
1925 dylib_ordinal,
1926 });
1927 if (bind_sym.weakRef()) {
1928 log.debug(" | marking as weak ref ", .{});
1929 }
1930 bind.entries.appendAssumeCapacity(.{
1931 .target = entry.target,
1932 .offset = base_offset,
1933 .segment_id = segment_index,
1934 .addend = 0,
1935 });
1936 }
1937 }
1938
1939 fn collectBindData(
1940 self: *Zld,
1941 bind: *Bind,
1942 ) !void {
1943 log.debug("collecting bind data", .{});
1944
1945 // First, unpack GOT section
1946 if (self.getSectionByName("__DATA_CONST", "__got")) |sect_id| {
1947 try self.collectBindDataFromContainer(sect_id, bind, self.got_entries);
1948 }
1949
1950 // Next, unpack TLV pointers section
1951 if (self.getSectionByName("__DATA", "__thread_ptrs")) |sect_id| {
1952 try self.collectBindDataFromContainer(sect_id, bind, self.tlv_ptr_entries);
1953 }
1954
1955 // Finally, unpack the rest.
1956 const slice = self.sections.slice();
1957 for (slice.items(.header), 0..) |header, sect_id| {
1958 switch (header.type()) {
1959 macho.S_LITERAL_POINTERS,
1960 macho.S_REGULAR,
1961 macho.S_MOD_INIT_FUNC_POINTERS,
1962 macho.S_MOD_TERM_FUNC_POINTERS,
1963 => {},
1964 else => continue,
1965 }
1966
1967 const segment_index = slice.items(.segment_index)[sect_id];
1968 const segment = self.getSegment(@as(u8, @intCast(sect_id)));
1969 if (segment.maxprot & macho.PROT.WRITE == 0) continue;
1970
1971 const cpu_arch = self.options.target.cpu.arch;
1972 var atom_index = slice.items(.first_atom_index)[sect_id];
1973 if (atom_index == 0) continue;
1974
1975 log.debug("{s},{s}", .{ header.segName(), header.sectName() });
1976
1977 while (true) {
1978 const atom = self.getAtom(atom_index);
1979 const sym = self.getSymbol(atom.getSymbolWithLoc());
1980
1981 log.debug(" ATOM({d}, %{d}, '{s}')", .{ atom_index, atom.sym_index, self.getSymbolName(atom.getSymbolWithLoc()) });
1982
1983 const should_bind = blk: {
1984 if (self.dyld_private_sym_index) |sym_index| {
1985 if (atom.getFile() == null and atom.sym_index == sym_index) break :blk false;
1986 }
1987 break :blk true;
1988 };
1989
1990 if (should_bind) {
1991 const code = Atom.getAtomCode(self, atom_index);
1992 const relocs = Atom.getAtomRelocs(self, atom_index);
1993 const ctx = Atom.getRelocContext(self, atom_index);
1994
1995 for (relocs) |rel| {
1996 switch (cpu_arch) {
1997 .aarch64 => {
1998 const rel_type = @as(macho.reloc_type_arm64, @enumFromInt(rel.r_type));
1999 if (rel_type != .ARM64_RELOC_UNSIGNED) continue;
2000 if (rel.r_length != 3) continue;
2001 },
2002 .x86_64 => {
2003 const rel_type = @as(macho.reloc_type_x86_64, @enumFromInt(rel.r_type));
2004 if (rel_type != .X86_64_RELOC_UNSIGNED) continue;
2005 if (rel.r_length != 3) continue;
2006 },
2007 else => unreachable,
2008 }
2009
2010 const global = Atom.parseRelocTarget(self, .{
2011 .object_id = atom.getFile().?,
2012 .rel = rel,
2013 .code = code,
2014 .base_offset = ctx.base_offset,
2015 .base_addr = ctx.base_addr,
2016 });
2017 const bind_sym_name = self.getSymbolName(global);
2018 const bind_sym = self.getSymbol(global);
2019 if (!bind_sym.undf()) continue;
2020
2021 const base_offset = sym.n_value - segment.vmaddr;
2022 const rel_offset = @as(u32, @intCast(rel.r_address - ctx.base_offset));
2023 const offset = @as(u64, @intCast(base_offset + rel_offset));
2024 const addend = mem.readIntLittle(i64, code[rel_offset..][0..8]);
2025
2026 const dylib_ordinal = @divTrunc(@as(i16, @bitCast(bind_sym.n_desc)), macho.N_SYMBOL_RESOLVER);
2027 log.debug(" | bind at {x}, import('{s}') in dylib({d})", .{
2028 base_offset,
2029 bind_sym_name,
2030 dylib_ordinal,
2031 });
2032 log.debug(" | with addend {x}", .{addend});
2033 if (bind_sym.weakRef()) {
2034 log.debug(" | marking as weak ref ", .{});
2035 }
2036 try bind.entries.append(self.gpa, .{
2037 .target = global,
2038 .offset = offset,
2039 .segment_id = segment_index,
2040 .addend = addend,
2041 });
2042 }
2043 }
2044 if (atom.next_index) |next_index| {
2045 atom_index = next_index;
2046 } else break;
2047 }
2048 }
2049
2050 try bind.finalize(self.gpa, self);
2051 }
2052
2053 fn collectLazyBindData(self: *Zld, lazy_bind: *LazyBind) !void {
2054 const sect_id = self.getSectionByName("__DATA", "__la_symbol_ptr") orelse return;
2055
2056 log.debug("collecting lazy bind data", .{});
2057
2058 const slice = self.sections.slice();
2059 const segment_index = slice.items(.segment_index)[sect_id];
2060 const seg = self.getSegment(sect_id);
2061 var atom_index = slice.items(.first_atom_index)[sect_id];
2062
2063 // TODO: we actually don't need to store lazy pointer atoms as they are synthetically generated by the linker
2064 try lazy_bind.entries.ensureUnusedCapacity(self.gpa, self.stubs.items.len);
2065
2066 var count: u32 = 0;
2067 while (true) : (count += 1) {
2068 const atom = self.getAtom(atom_index);
2069
2070 log.debug(" ATOM(%{d}, '{s}')", .{ atom.sym_index, self.getSymbolName(atom.getSymbolWithLoc()) });
2071
2072 const sym = self.getSymbol(atom.getSymbolWithLoc());
2073 const base_offset = sym.n_value - seg.vmaddr;
2074
2075 const stub_entry = self.stubs.items[count];
2076 const bind_sym = stub_entry.getTargetSymbol(self);
2077 const bind_sym_name = stub_entry.getTargetSymbolName(self);
2078 const dylib_ordinal = @divTrunc(@as(i16, @bitCast(bind_sym.n_desc)), macho.N_SYMBOL_RESOLVER);
2079 log.debug(" | lazy bind at {x}, import('{s}') in dylib({d})", .{
2080 base_offset,
2081 bind_sym_name,
2082 dylib_ordinal,
2083 });
2084 if (bind_sym.weakRef()) {
2085 log.debug(" | marking as weak ref ", .{});
2086 }
2087 lazy_bind.entries.appendAssumeCapacity(.{
2088 .target = stub_entry.target,
2089 .offset = base_offset,
2090 .segment_id = segment_index,
2091 .addend = 0,
2092 });
2093
2094 if (atom.next_index) |next_index| {
2095 atom_index = next_index;
2096 } else break;
2097 }
2098
2099 try lazy_bind.finalize(self.gpa, self);
2100 }
2101
2102 fn collectExportData(self: *Zld, trie: *Trie) !void {
2103 const gpa = self.gpa;
2104
2105 // TODO handle macho.EXPORT_SYMBOL_FLAGS_REEXPORT and macho.EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER.
2106 log.debug("collecting export data", .{});
2107
2108 const segment_index = self.getSegmentByName("__TEXT").?;
2109 const exec_segment = self.segments.items[segment_index];
2110 const base_address = exec_segment.vmaddr;
2111
2112 for (self.globals.items) |global| {
2113 const sym = self.getSymbol(global);
2114 if (sym.undf()) continue;
2115 if (sym.n_desc == N_DEAD) continue;
2116
2117 const sym_name = self.getSymbolName(global);
2118 log.debug(" (putting '{s}' defined at 0x{x})", .{ sym_name, sym.n_value });
2119 try trie.put(gpa, .{
2120 .name = sym_name,
2121 .vmaddr_offset = sym.n_value - base_address,
2122 .export_flags = macho.EXPORT_SYMBOL_FLAGS_KIND_REGULAR,
2123 });
2124 }
2125
2126 try trie.finalize(gpa);
2127 }
2128
2129 fn writeDyldInfoData(self: *Zld) !void {
2130 const gpa = self.gpa;
2131
2132 var rebase = Rebase{};
2133 defer rebase.deinit(gpa);
2134 try self.collectRebaseData(&rebase);
2135
2136 var bind = Bind{};
2137 defer bind.deinit(gpa);
2138 try self.collectBindData(&bind);
2139
2140 var lazy_bind = LazyBind{};
2141 defer lazy_bind.deinit(gpa);
2142 try self.collectLazyBindData(&lazy_bind);
2143
2144 var trie = Trie{};
2145 defer trie.deinit(gpa);
2146 try trie.init(gpa);
2147 try self.collectExportData(&trie);
2148
2149 const link_seg = self.getLinkeditSegmentPtr();
2150 assert(mem.isAlignedGeneric(u64, link_seg.fileoff, @alignOf(u64)));
2151 const rebase_off = link_seg.fileoff;
2152 const rebase_size = rebase.size();
2153 const rebase_size_aligned = mem.alignForward(u64, rebase_size, @alignOf(u64));
2154 log.debug("writing rebase info from 0x{x} to 0x{x}", .{ rebase_off, rebase_off + rebase_size_aligned });
2155
2156 const bind_off = rebase_off + rebase_size_aligned;
2157 const bind_size = bind.size();
2158 const bind_size_aligned = mem.alignForward(u64, bind_size, @alignOf(u64));
2159 log.debug("writing bind info from 0x{x} to 0x{x}", .{ bind_off, bind_off + bind_size_aligned });
2160
2161 const lazy_bind_off = bind_off + bind_size_aligned;
2162 const lazy_bind_size = lazy_bind.size();
2163 const lazy_bind_size_aligned = mem.alignForward(u64, lazy_bind_size, @alignOf(u64));
2164 log.debug("writing lazy bind info from 0x{x} to 0x{x}", .{
2165 lazy_bind_off,
2166 lazy_bind_off + lazy_bind_size_aligned,
2167 });
2168
2169 const export_off = lazy_bind_off + lazy_bind_size_aligned;
2170 const export_size = trie.size;
2171 const export_size_aligned = mem.alignForward(u64, export_size, @alignOf(u64));
2172 log.debug("writing export trie from 0x{x} to 0x{x}", .{ export_off, export_off + export_size_aligned });
2173
2174 const needed_size = math.cast(usize, export_off + export_size_aligned - rebase_off) orelse
2175 return error.Overflow;
2176 link_seg.filesize = needed_size;
2177 assert(mem.isAlignedGeneric(u64, link_seg.fileoff + link_seg.filesize, @alignOf(u64)));
2178
2179 var buffer = try gpa.alloc(u8, needed_size);
2180 defer gpa.free(buffer);
2181 @memset(buffer, 0);
2182
2183 var stream = std.io.fixedBufferStream(buffer);
2184 const writer = stream.writer();
2185
2186 try rebase.write(writer);
2187 try stream.seekTo(bind_off - rebase_off);
2188
2189 try bind.write(writer);
2190 try stream.seekTo(lazy_bind_off - rebase_off);
2191
2192 try lazy_bind.write(writer);
2193 try stream.seekTo(export_off - rebase_off);
2194
2195 _ = try trie.write(writer);
2196
2197 log.debug("writing dyld info from 0x{x} to 0x{x}", .{
2198 rebase_off,
2199 rebase_off + needed_size,
2200 });
2201
2202 try self.file.pwriteAll(buffer, rebase_off);
2203 try self.populateLazyBindOffsetsInStubHelper(lazy_bind);
2204
2205 self.dyld_info_cmd.rebase_off = @as(u32, @intCast(rebase_off));
2206 self.dyld_info_cmd.rebase_size = @as(u32, @intCast(rebase_size_aligned));
2207 self.dyld_info_cmd.bind_off = @as(u32, @intCast(bind_off));
2208 self.dyld_info_cmd.bind_size = @as(u32, @intCast(bind_size_aligned));
2209 self.dyld_info_cmd.lazy_bind_off = @as(u32, @intCast(lazy_bind_off));
2210 self.dyld_info_cmd.lazy_bind_size = @as(u32, @intCast(lazy_bind_size_aligned));
2211 self.dyld_info_cmd.export_off = @as(u32, @intCast(export_off));
2212 self.dyld_info_cmd.export_size = @as(u32, @intCast(export_size_aligned));
2213 }
2214
2215 fn populateLazyBindOffsetsInStubHelper(self: *Zld, lazy_bind: LazyBind) !void {
2216 if (lazy_bind.size() == 0) return;
2217
2218 const stub_helper_section_index = self.getSectionByName("__TEXT", "__stub_helper").?;
2219 assert(self.stub_helper_preamble_sym_index != null);
2220
2221 const section = self.sections.get(stub_helper_section_index);
2222 const stub_offset = stub_helpers.calcStubOffsetInStubHelper(self.options.target.cpu.arch);
2223 const header = section.header;
2224 var atom_index = section.first_atom_index;
2225 atom_index = self.getAtom(atom_index).next_index.?; // skip preamble
2226
2227 var index: usize = 0;
2228 while (true) {
2229 const atom = self.getAtom(atom_index);
2230 const atom_sym = self.getSymbol(atom.getSymbolWithLoc());
2231 const file_offset = header.offset + atom_sym.n_value - header.addr + stub_offset;
2232 const bind_offset = lazy_bind.offsets.items[index];
2233
2234 log.debug("writing lazy bind offset 0x{x} in stub helper at 0x{x}", .{ bind_offset, file_offset });
2235
2236 try self.file.pwriteAll(mem.asBytes(&bind_offset), file_offset);
2237
2238 if (atom.next_index) |next_index| {
2239 atom_index = next_index;
2240 index += 1;
2241 } else break;
2242 }
2243 }
2244
2245 const asc_u64 = std.sort.asc(u64);
2246
2247 fn addSymbolToFunctionStarts(self: *Zld, sym_loc: SymbolWithLoc, addresses: *std.ArrayList(u64)) !void {
2248 const sym = self.getSymbol(sym_loc);
2249 if (sym.n_strx == 0) return;
2250 if (sym.n_desc == N_DEAD) return;
2251 if (self.symbolIsTemp(sym_loc)) return;
2252 try addresses.append(sym.n_value);
2253 }
2254
2255 fn writeFunctionStarts(self: *Zld) !void {
2256 const text_seg_index = self.getSegmentByName("__TEXT") orelse return;
2257 const text_seg = self.segments.items[text_seg_index];
2258
2259 const gpa = self.gpa;
2260
2261 // We need to sort by address first
2262 var addresses = std.ArrayList(u64).init(gpa);
2263 defer addresses.deinit();
2264
2265 for (self.objects.items) |object| {
2266 for (object.exec_atoms.items) |atom_index| {
2267 const atom = self.getAtom(atom_index);
2268 const sym_loc = atom.getSymbolWithLoc();
2269 try self.addSymbolToFunctionStarts(sym_loc, &addresses);
2270
2271 var it = Atom.getInnerSymbolsIterator(self, atom_index);
2272 while (it.next()) |inner_sym_loc| {
2273 try self.addSymbolToFunctionStarts(inner_sym_loc, &addresses);
2274 }
2275 }
2276 }
2277
2278 mem.sort(u64, addresses.items, {}, asc_u64);
2279
2280 var offsets = std.ArrayList(u32).init(gpa);
2281 defer offsets.deinit();
2282 try offsets.ensureTotalCapacityPrecise(addresses.items.len);
2283
2284 var last_off: u32 = 0;
2285 for (addresses.items) |addr| {
2286 const offset = @as(u32, @intCast(addr - text_seg.vmaddr));
2287 const diff = offset - last_off;
2288
2289 if (diff == 0) continue;
1pub fn linkWithZld(
2 macho_file: *MachO,
3 comp: *Compilation,
4 prog_node: *std.Progress.Node,
5) link.File.FlushError!void {
6 const tracy = trace(@src());
7 defer tracy.end();
22908
2291 offsets.appendAssumeCapacity(diff);
2292 last_off = offset;
2293 }
9 const gpa = macho_file.base.allocator;
10 const options = &macho_file.base.options;
11 const target = options.target;
229412
2295 var buffer = std.ArrayList(u8).init(gpa);
2296 defer buffer.deinit();
13 var arena_allocator = std.heap.ArenaAllocator.init(gpa);
14 defer arena_allocator.deinit();
15 const arena = arena_allocator.allocator();
229716
2298 const max_size = @as(usize, @intCast(offsets.items.len * @sizeOf(u64)));
2299 try buffer.ensureTotalCapacity(max_size);
17 const directory = options.emit.?.directory; // Just an alias to make it shorter to type.
18 const full_out_path = try directory.join(arena, &[_][]const u8{options.emit.?.sub_path});
230019
2301 for (offsets.items) |offset| {
2302 try std.leb.writeULEB128(buffer.writer(), offset);
2303 }
20 // If there is no Zig code to compile, then we should skip flushing the output file because it
21 // will not be part of the linker line anyway.
22 const module_obj_path: ?[]const u8 = if (options.module != null) blk: {
23 try macho_file.flushModule(comp, prog_node);
230424
2305 const link_seg = self.getLinkeditSegmentPtr();
2306 const offset = link_seg.fileoff + link_seg.filesize;
2307 assert(mem.isAlignedGeneric(u64, offset, @alignOf(u64)));
2308 const needed_size = buffer.items.len;
2309 const needed_size_aligned = mem.alignForward(u64, needed_size, @alignOf(u64));
2310 const padding = math.cast(usize, needed_size_aligned - needed_size) orelse return error.Overflow;
2311 if (padding > 0) {
2312 try buffer.ensureUnusedCapacity(padding);
2313 buffer.appendNTimesAssumeCapacity(0, padding);
25 if (fs.path.dirname(full_out_path)) |dirname| {
26 break :blk try fs.path.join(arena, &.{ dirname, macho_file.base.intermediary_basename.? });
27 } else {
28 break :blk macho_file.base.intermediary_basename.?;
231429 }
2315 link_seg.filesize = offset + needed_size_aligned - link_seg.fileoff;
2316
2317 log.debug("writing function starts info from 0x{x} to 0x{x}", .{ offset, offset + needed_size_aligned });
2318
2319 try self.file.pwriteAll(buffer.items, offset);
30 } else null;
232031
2321 self.function_starts_cmd.dataoff = @as(u32, @intCast(offset));
2322 self.function_starts_cmd.datasize = @as(u32, @intCast(needed_size_aligned));
2323 }
32 var sub_prog_node = prog_node.start("MachO Flush", 0);
33 sub_prog_node.activate();
34 sub_prog_node.context.refresh();
35 defer sub_prog_node.end();
232436
2325 fn filterDataInCode(
2326 dices: []const macho.data_in_code_entry,
2327 start_addr: u64,
2328 end_addr: u64,
2329 ) []const macho.data_in_code_entry {
2330 const Predicate = struct {
2331 addr: u64,
37 const cpu_arch = target.cpu.arch;
38 const is_lib = options.output_mode == .Lib;
39 const is_dyn_lib = options.link_mode == .Dynamic and is_lib;
40 const is_exe_or_dyn_lib = is_dyn_lib or options.output_mode == .Exe;
41 const stack_size = options.stack_size_override orelse 0;
42 const is_debug_build = options.optimize_mode == .Debug;
43 const gc_sections = options.gc_sections orelse !is_debug_build;
233244
2333 pub fn predicate(self: @This(), dice: macho.data_in_code_entry) bool {
2334 return dice.offset >= self.addr;
2335 }
2336 };
45 const id_symlink_basename = "zld.id";
233746
2338 const start = lsearch(macho.data_in_code_entry, dices, Predicate{ .addr = start_addr });
2339 const end = lsearch(macho.data_in_code_entry, dices[start..], Predicate{ .addr = end_addr }) + start;
47 var man: Cache.Manifest = undefined;
48 defer if (!options.disable_lld_caching) man.deinit();
234049
2341 return dices[start..end];
2342 }
50 var digest: [Cache.hex_digest_len]u8 = undefined;
234351
2344 fn writeDataInCode(self: *Zld) !void {
2345 var out_dice = std.ArrayList(macho.data_in_code_entry).init(self.gpa);
2346 defer out_dice.deinit();
2347
2348 const text_sect_id = self.getSectionByName("__TEXT", "__text") orelse return;
2349 const text_sect_header = self.sections.items(.header)[text_sect_id];
2350
2351 for (self.objects.items) |object| {
2352 if (!object.hasDataInCode()) continue;
2353 const dice = object.data_in_code.items;
2354 try out_dice.ensureUnusedCapacity(dice.len);
2355
2356 for (object.exec_atoms.items) |atom_index| {
2357 const atom = self.getAtom(atom_index);
2358 const sym = self.getSymbol(atom.getSymbolWithLoc());
2359 if (sym.n_desc == N_DEAD) continue;
2360
2361 const source_addr = if (object.getSourceSymbol(atom.sym_index)) |source_sym|
2362 source_sym.n_value
2363 else blk: {
2364 const nbase = @as(u32, @intCast(object.in_symtab.?.len));
2365 const source_sect_id = @as(u8, @intCast(atom.sym_index - nbase));
2366 break :blk object.getSourceSection(source_sect_id).addr;
2367 };
2368 const filtered_dice = filterDataInCode(dice, source_addr, source_addr + atom.size);
2369 const base = math.cast(u32, sym.n_value - text_sect_header.addr + text_sect_header.offset) orelse
2370 return error.Overflow;
52 if (!options.disable_lld_caching) {
53 man = comp.cache_parent.obtain();
237154
2372 for (filtered_dice) |single| {
2373 const offset = math.cast(u32, single.offset - source_addr + base) orelse
2374 return error.Overflow;
2375 out_dice.appendAssumeCapacity(.{
2376 .offset = offset,
2377 .length = single.length,
2378 .kind = single.kind,
2379 });
2380 }
2381 }
2382 }
55 // We are about to obtain this lock, so here we give other processes a chance first.
56 macho_file.base.releaseLock();
238357
2384 const seg = self.getLinkeditSegmentPtr();
2385 const offset = seg.fileoff + seg.filesize;
2386 assert(mem.isAlignedGeneric(u64, offset, @alignOf(u64)));
2387 const needed_size = out_dice.items.len * @sizeOf(macho.data_in_code_entry);
2388 const needed_size_aligned = mem.alignForward(u64, needed_size, @alignOf(u64));
2389 seg.filesize = offset + needed_size_aligned - seg.fileoff;
58 comptime assert(Compilation.link_hash_implementation_version == 10);
239059
2391 const buffer = try self.gpa.alloc(u8, math.cast(usize, needed_size_aligned) orelse return error.Overflow);
2392 defer self.gpa.free(buffer);
2393 {
2394 const src = mem.sliceAsBytes(out_dice.items);
2395 @memcpy(buffer[0..src.len], src);
2396 @memset(buffer[src.len..], 0);
60 for (options.objects) |obj| {
61 _ = try man.addFile(obj.path, null);
62 man.hash.add(obj.must_link);
239763 }
2398
2399 log.debug("writing data-in-code from 0x{x} to 0x{x}", .{ offset, offset + needed_size_aligned });
2400
2401 try self.file.pwriteAll(buffer, offset);
2402
2403 self.data_in_code_cmd.dataoff = @as(u32, @intCast(offset));
2404 self.data_in_code_cmd.datasize = @as(u32, @intCast(needed_size_aligned));
2405 }
2406
2407 fn writeSymtabs(self: *Zld) !void {
2408 var ctx = try self.writeSymtab();
2409 defer ctx.imports_table.deinit();
2410 try self.writeDysymtab(ctx);
2411 try self.writeStrtab();
2412 }
2413
2414 fn addLocalToSymtab(self: *Zld, sym_loc: SymbolWithLoc, locals: *std.ArrayList(macho.nlist_64)) !void {
2415 const sym = self.getSymbol(sym_loc);
2416 if (sym.n_strx == 0) return; // no name, skip
2417 if (sym.n_desc == N_DEAD) return; // garbage-collected, skip
2418 if (sym.ext()) return; // an export lands in its own symtab section, skip
2419 if (self.symbolIsTemp(sym_loc)) return; // local temp symbol, skip
2420
2421 var out_sym = sym;
2422 out_sym.n_strx = try self.strtab.insert(self.gpa, self.getSymbolName(sym_loc));
2423 try locals.append(out_sym);
2424 }
2425
2426 fn writeSymtab(self: *Zld) !SymtabCtx {
2427 const gpa = self.gpa;
2428
2429 var locals = std.ArrayList(macho.nlist_64).init(gpa);
2430 defer locals.deinit();
2431
2432 for (self.objects.items) |object| {
2433 for (object.atoms.items) |atom_index| {
2434 const atom = self.getAtom(atom_index);
2435 const sym_loc = atom.getSymbolWithLoc();
2436 try self.addLocalToSymtab(sym_loc, &locals);
2437
2438 var it = Atom.getInnerSymbolsIterator(self, atom_index);
2439 while (it.next()) |inner_sym_loc| {
2440 try self.addLocalToSymtab(inner_sym_loc, &locals);
2441 }
2442 }
64 for (comp.c_object_table.keys()) |key| {
65 _ = try man.addFile(key.status.success.object_path, null);
244366 }
2444
2445 var exports = std.ArrayList(macho.nlist_64).init(gpa);
2446 defer exports.deinit();
2447
2448 for (self.globals.items) |global| {
2449 const sym = self.getSymbol(global);
2450 if (sym.undf()) continue; // import, skip
2451 if (sym.n_desc == N_DEAD) continue;
2452
2453 var out_sym = sym;
2454 out_sym.n_strx = try self.strtab.insert(gpa, self.getSymbolName(global));
2455 try exports.append(out_sym);
67 try man.addOptionalFile(module_obj_path);
68 // We can skip hashing libc and libc++ components that we are in charge of building from Zig
69 // installation sources because they are always a product of the compiler version + target information.
70 man.hash.add(stack_size);
71 man.hash.addOptional(options.pagezero_size);
72 man.hash.addOptional(options.headerpad_size);
73 man.hash.add(options.headerpad_max_install_names);
74 man.hash.add(gc_sections);
75 man.hash.add(options.dead_strip_dylibs);
76 man.hash.add(options.strip);
77 man.hash.addListOfBytes(options.lib_dirs);
78 man.hash.addListOfBytes(options.framework_dirs);
79 try link.hashAddFrameworks(&man, options.frameworks);
80 man.hash.addListOfBytes(options.rpath_list);
81 if (is_dyn_lib) {
82 man.hash.addOptionalBytes(options.install_name);
83 man.hash.addOptional(options.version);
245684 }
85 try link.hashAddSystemLibs(&man, options.system_libs);
86 man.hash.addOptionalBytes(options.sysroot);
87 man.hash.addListOfBytes(options.force_undefined_symbols.keys());
88 try man.addOptionalFile(options.entitlements);
245789
2458 var imports = std.ArrayList(macho.nlist_64).init(gpa);
2459 defer imports.deinit();
2460
2461 var imports_table = std.AutoHashMap(SymbolWithLoc, u32).init(gpa);
2462
2463 for (self.globals.items) |global| {
2464 const sym = self.getSymbol(global);
2465 if (!sym.undf()) continue; // not an import, skip
2466 if (sym.n_desc == N_DEAD) continue;
2467
2468 const new_index = @as(u32, @intCast(imports.items.len));
2469 var out_sym = sym;
2470 out_sym.n_strx = try self.strtab.insert(gpa, self.getSymbolName(global));
2471 try imports.append(out_sym);
2472 try imports_table.putNoClobber(global, new_index);
2473 }
90 // We don't actually care whether it's a cache hit or miss; we just
91 // need the digest and the lock.
92 _ = try man.hit();
93 digest = man.final();
247494
2475 // We generate stabs last in order to ensure that the strtab always has debug info
2476 // strings trailing
2477 if (!self.options.strip) {
2478 for (self.objects.items) |object| {
2479 try self.generateSymbolStabs(object, &locals);
2480 }
95 var prev_digest_buf: [digest.len]u8 = undefined;
96 const prev_digest: []u8 = Cache.readSmallFile(
97 directory.handle,
98 id_symlink_basename,
99 &prev_digest_buf,
100 ) catch |err| blk: {
101 log.debug("MachO Zld new_digest={s} error: {s}", .{
102 std.fmt.fmtSliceHexLower(&digest),
103 @errorName(err),
104 });
105 // Handle this as a cache miss.
106 break :blk prev_digest_buf[0..0];
107 };
108 if (mem.eql(u8, prev_digest, &digest)) {
109 // Hot diggity dog! The output binary is already there.
110 log.debug("MachO Zld digest={s} match - skipping invocation", .{
111 std.fmt.fmtSliceHexLower(&digest),
112 });
113 macho_file.base.lock = man.toOwnedLock();
114 return;
2481115 }
116 log.debug("MachO Zld prev_digest={s} new_digest={s}", .{
117 std.fmt.fmtSliceHexLower(prev_digest),
118 std.fmt.fmtSliceHexLower(&digest),
119 });
2482120
2483 const nlocals = @as(u32, @intCast(locals.items.len));
2484 const nexports = @as(u32, @intCast(exports.items.len));
2485 const nimports = @as(u32, @intCast(imports.items.len));
2486 const nsyms = nlocals + nexports + nimports;
2487
2488 const seg = self.getLinkeditSegmentPtr();
2489 const offset = seg.fileoff + seg.filesize;
2490 assert(mem.isAlignedGeneric(u64, offset, @alignOf(u64)));
2491 const needed_size = nsyms * @sizeOf(macho.nlist_64);
2492 seg.filesize = offset + needed_size - seg.fileoff;
2493 assert(mem.isAlignedGeneric(u64, seg.fileoff + seg.filesize, @alignOf(u64)));
2494
2495 var buffer = std.ArrayList(u8).init(gpa);
2496 defer buffer.deinit();
2497 try buffer.ensureTotalCapacityPrecise(needed_size);
2498 buffer.appendSliceAssumeCapacity(mem.sliceAsBytes(locals.items));
2499 buffer.appendSliceAssumeCapacity(mem.sliceAsBytes(exports.items));
2500 buffer.appendSliceAssumeCapacity(mem.sliceAsBytes(imports.items));
2501
2502 log.debug("writing symtab from 0x{x} to 0x{x}", .{ offset, offset + needed_size });
2503 try self.file.pwriteAll(buffer.items, offset);
2504
2505 self.symtab_cmd.symoff = @as(u32, @intCast(offset));
2506 self.symtab_cmd.nsyms = nsyms;
2507
2508 return SymtabCtx{
2509 .nlocalsym = nlocals,
2510 .nextdefsym = nexports,
2511 .nundefsym = nimports,
2512 .imports_table = imports_table,
121 // We are about to change the output file to be different, so we invalidate the build hash now.
122 directory.handle.deleteFile(id_symlink_basename) catch |err| switch (err) {
123 error.FileNotFound => {},
124 else => |e| return e,
2513125 };
2514126 }
2515127
2516 fn writeStrtab(self: *Zld) !void {
2517 const seg = self.getLinkeditSegmentPtr();
2518 const offset = seg.fileoff + seg.filesize;
2519 assert(mem.isAlignedGeneric(u64, offset, @alignOf(u64)));
2520 const needed_size = self.strtab.buffer.items.len;
2521 const needed_size_aligned = mem.alignForward(u64, needed_size, @alignOf(u64));
2522 seg.filesize = offset + needed_size_aligned - seg.fileoff;
2523
2524 log.debug("writing string table from 0x{x} to 0x{x}", .{ offset, offset + needed_size_aligned });
2525
2526 const buffer = try self.gpa.alloc(u8, math.cast(usize, needed_size_aligned) orelse return error.Overflow);
2527 defer self.gpa.free(buffer);
2528 @memcpy(buffer[0..self.strtab.buffer.items.len], self.strtab.buffer.items);
2529 @memset(buffer[self.strtab.buffer.items.len..], 0);
2530
2531 try self.file.pwriteAll(buffer, offset);
2532
2533 self.symtab_cmd.stroff = @as(u32, @intCast(offset));
2534 self.symtab_cmd.strsize = @as(u32, @intCast(needed_size_aligned));
2535 }
2536
2537 const SymtabCtx = struct {
2538 nlocalsym: u32,
2539 nextdefsym: u32,
2540 nundefsym: u32,
2541 imports_table: std.AutoHashMap(SymbolWithLoc, u32),
2542 };
2543
2544 fn writeDysymtab(self: *Zld, ctx: SymtabCtx) !void {
2545 const gpa = self.gpa;
2546 const nstubs = @as(u32, @intCast(self.stubs.items.len));
2547 const ngot_entries = @as(u32, @intCast(self.got_entries.items.len));
2548 const nindirectsyms = nstubs * 2 + ngot_entries;
2549 const iextdefsym = ctx.nlocalsym;
2550 const iundefsym = iextdefsym + ctx.nextdefsym;
2551
2552 const seg = self.getLinkeditSegmentPtr();
2553 const offset = seg.fileoff + seg.filesize;
2554 assert(mem.isAlignedGeneric(u64, offset, @alignOf(u64)));
2555 const needed_size = nindirectsyms * @sizeOf(u32);
2556 const needed_size_aligned = mem.alignForward(u64, needed_size, @alignOf(u64));
2557 seg.filesize = offset + needed_size_aligned - seg.fileoff;
2558
2559 log.debug("writing indirect symbol table from 0x{x} to 0x{x}", .{ offset, offset + needed_size_aligned });
2560
2561 var buf = std.ArrayList(u8).init(gpa);
2562 defer buf.deinit();
2563 try buf.ensureTotalCapacityPrecise(math.cast(usize, needed_size_aligned) orelse return error.Overflow);
2564 const writer = buf.writer();
2565
2566 if (self.getSectionByName("__TEXT", "__stubs")) |sect_id| {
2567 const stubs = &self.sections.items(.header)[sect_id];
2568 stubs.reserved1 = 0;
2569 for (self.stubs.items) |entry| {
2570 const target_sym = entry.getTargetSymbol(self);
2571 assert(target_sym.undf());
2572 try writer.writeIntLittle(u32, iundefsym + ctx.imports_table.get(entry.target).?);
128 if (options.output_mode == .Obj) {
129 // LLD's MachO driver does not support the equivalent of `-r` so we do a simple file copy
130 // here. TODO: think carefully about how we can avoid this redundant operation when doing
131 // build-obj. See also the corresponding TODO in linkAsArchive.
132 const the_object_path = blk: {
133 if (options.objects.len != 0) {
134 break :blk options.objects[0].path;
2573135 }
2574 }
2575136
2576 if (self.getSectionByName("__DATA_CONST", "__got")) |sect_id| {
2577 const got = &self.sections.items(.header)[sect_id];
2578 got.reserved1 = nstubs;
2579 for (self.got_entries.items) |entry| {
2580 const target_sym = entry.getTargetSymbol(self);
2581 if (target_sym.undf()) {
2582 try writer.writeIntLittle(u32, iundefsym + ctx.imports_table.get(entry.target).?);
2583 } else {
2584 try writer.writeIntLittle(u32, macho.INDIRECT_SYMBOL_LOCAL);
2585 }
2586 }
2587 }
137 if (comp.c_object_table.count() != 0)
138 break :blk comp.c_object_table.keys()[0].status.success.object_path;
2588139
2589 if (self.getSectionByName("__DATA", "__la_symbol_ptr")) |sect_id| {
2590 const la_symbol_ptr = &self.sections.items(.header)[sect_id];
2591 la_symbol_ptr.reserved1 = nstubs + ngot_entries;
2592 for (self.stubs.items) |entry| {
2593 const target_sym = entry.getTargetSymbol(self);
2594 assert(target_sym.undf());
2595 try writer.writeIntLittle(u32, iundefsym + ctx.imports_table.get(entry.target).?);
2596 }
2597 }
140 if (module_obj_path) |p|
141 break :blk p;
2598142
2599 const padding = math.cast(usize, needed_size_aligned - needed_size) orelse return error.Overflow;
2600 if (padding > 0) {
2601 buf.appendNTimesAssumeCapacity(0, padding);
143 // TODO I think this is unreachable. Audit this situation when solving the above TODO
144 // regarding eliding redundant object -> object transformations.
145 return error.NoObjectsToLink;
146 };
147 // This can happen when using --enable-cache and using the stage1 backend. In this case
148 // we can skip the file copy.
149 if (!mem.eql(u8, the_object_path, full_out_path)) {
150 try fs.cwd().copyFile(the_object_path, fs.cwd(), full_out_path, .{});
2602151 }
152 } else {
153 const sub_path = options.emit.?.sub_path;
2603154
2604 assert(buf.items.len == needed_size_aligned);
2605 try self.file.pwriteAll(buf.items, offset);
2606
2607 self.dysymtab_cmd.nlocalsym = ctx.nlocalsym;
2608 self.dysymtab_cmd.iextdefsym = iextdefsym;
2609 self.dysymtab_cmd.nextdefsym = ctx.nextdefsym;
2610 self.dysymtab_cmd.iundefsym = iundefsym;
2611 self.dysymtab_cmd.nundefsym = ctx.nundefsym;
2612 self.dysymtab_cmd.indirectsymoff = @as(u32, @intCast(offset));
2613 self.dysymtab_cmd.nindirectsyms = nindirectsyms;
2614 }
2615
2616 fn writeUuid(self: *Zld, comp: *const Compilation, uuid_cmd_offset: u32, has_codesig: bool) !void {
2617 const file_size = if (!has_codesig) blk: {
2618 const seg = self.getLinkeditSegmentPtr();
2619 break :blk seg.fileoff + seg.filesize;
2620 } else self.codesig_cmd.dataoff;
2621 try calcUuid(comp, self.file, file_size, &self.uuid_cmd.uuid);
2622 const offset = uuid_cmd_offset + @sizeOf(macho.load_command);
2623 try self.file.pwriteAll(&self.uuid_cmd.uuid, offset);
2624 }
2625
2626 fn writeCodeSignaturePadding(self: *Zld, code_sig: *CodeSignature) !void {
2627 const seg = self.getLinkeditSegmentPtr();
2628 // Code signature data has to be 16-bytes aligned for Apple tools to recognize the file
2629 // https://github.com/opensource-apple/cctools/blob/fdb4825f303fd5c0751be524babd32958181b3ed/libstuff/checkout.c#L271
2630 const offset = mem.alignForward(u64, seg.fileoff + seg.filesize, 16);
2631 const needed_size = code_sig.estimateSize(offset);
2632 seg.filesize = offset + needed_size - seg.fileoff;
2633 seg.vmsize = mem.alignForward(u64, seg.filesize, self.page_size);
2634 log.debug("writing code signature padding from 0x{x} to 0x{x}", .{ offset, offset + needed_size });
2635 // Pad out the space. We need to do this to calculate valid hashes for everything in the file
2636 // except for code signature data.
2637 try self.file.pwriteAll(&[_]u8{0}, offset + needed_size - 1);
2638
2639 self.codesig_cmd.dataoff = @as(u32, @intCast(offset));
2640 self.codesig_cmd.datasize = @as(u32, @intCast(needed_size));
2641 }
2642
2643 fn writeCodeSignature(self: *Zld, comp: *const Compilation, code_sig: *CodeSignature) !void {
2644 const seg_id = self.getSegmentByName("__TEXT").?;
2645 const seg = self.segments.items[seg_id];
2646
2647 var buffer = std.ArrayList(u8).init(self.gpa);
2648 defer buffer.deinit();
2649 try buffer.ensureTotalCapacityPrecise(code_sig.size());
2650 try code_sig.writeAdhocSignature(comp, .{
2651 .file = self.file,
2652 .exec_seg_base = seg.fileoff,
2653 .exec_seg_limit = seg.filesize,
2654 .file_size = self.codesig_cmd.dataoff,
2655 .output_mode = self.options.output_mode,
2656 }, buffer.writer());
2657 assert(buffer.items.len == code_sig.size());
155 const old_file = macho_file.base.file; // TODO is this needed at all?
156 defer macho_file.base.file = old_file;
2658157
2659 log.debug("writing code signature from 0x{x} to 0x{x}", .{
2660 self.codesig_cmd.dataoff,
2661 self.codesig_cmd.dataoff + buffer.items.len,
158 const file = try directory.handle.createFile(sub_path, .{
159 .truncate = true,
160 .read = true,
161 .mode = link.determineMode(options.*),
2662162 });
163 defer file.close();
164 macho_file.base.file = file;
2663165
2664 try self.file.pwriteAll(buffer.items, self.codesig_cmd.dataoff);
2665 }
166 // Index 0 is always a null symbol.
167 try macho_file.locals.append(gpa, .{
168 .n_strx = 0,
169 .n_type = 0,
170 .n_sect = 0,
171 .n_desc = 0,
172 .n_value = 0,
173 });
174 try macho_file.strtab.buffer.append(gpa, 0);
2666175
2667 /// Writes Mach-O file header.
2668 fn writeHeader(self: *Zld, ncmds: u32, sizeofcmds: u32) !void {
2669 var header: macho.mach_header_64 = .{};
2670 header.flags = macho.MH_NOUNDEFS | macho.MH_DYLDLINK | macho.MH_PIE | macho.MH_TWOLEVEL;
176 // Positional arguments to the linker such as object files and static archives.
177 var positionals = std.ArrayList(Compilation.LinkObject).init(arena);
178 try positionals.ensureUnusedCapacity(options.objects.len);
179 positionals.appendSliceAssumeCapacity(options.objects);
2671180
2672 switch (self.options.target.cpu.arch) {
2673 .aarch64 => {
2674 header.cputype = macho.CPU_TYPE_ARM64;
2675 header.cpusubtype = macho.CPU_SUBTYPE_ARM_ALL;
2676 },
2677 .x86_64 => {
2678 header.cputype = macho.CPU_TYPE_X86_64;
2679 header.cpusubtype = macho.CPU_SUBTYPE_X86_64_ALL;
2680 },
2681 else => return error.UnsupportedCpuArchitecture,
181 for (comp.c_object_table.keys()) |key| {
182 try positionals.append(.{ .path = key.status.success.object_path });
2682183 }
2683184
2684 switch (self.options.output_mode) {
2685 .Exe => {
2686 header.filetype = macho.MH_EXECUTE;
2687 },
2688 .Lib => {
2689 // By this point, it can only be a dylib.
2690 header.filetype = macho.MH_DYLIB;
2691 header.flags |= macho.MH_NO_REEXPORTED_DYLIBS;
2692 },
2693 else => unreachable,
185 if (module_obj_path) |p| {
186 try positionals.append(.{ .path = p });
2694187 }
2695188
2696 if (self.getSectionByName("__DATA", "__thread_vars")) |sect_id| {
2697 header.flags |= macho.MH_HAS_TLV_DESCRIPTORS;
2698 if (self.sections.items(.header)[sect_id].size > 0) {
2699 header.flags |= macho.MH_HAS_TLV_DESCRIPTORS;
2700 }
189 if (comp.compiler_rt_lib) |lib| {
190 try positionals.append(.{ .path = lib.full_object_path });
2701191 }
2702192
2703 header.ncmds = ncmds;
2704 header.sizeofcmds = sizeofcmds;
2705
2706 log.debug("writing Mach-O header {}", .{header});
2707
2708 try self.file.pwriteAll(mem.asBytes(&header), 0);
2709 }
2710
2711 pub fn makeStaticString(bytes: []const u8) [16]u8 {
2712 var buf = [_]u8{0} ** 16;
2713 @memcpy(buf[0..bytes.len], bytes);
2714 return buf;
2715 }
2716
2717 pub fn getAtomPtr(self: *Zld, atom_index: AtomIndex) *Atom {
2718 assert(atom_index < self.atoms.items.len);
2719 return &self.atoms.items[atom_index];
2720 }
2721
2722 pub fn getAtom(self: Zld, atom_index: AtomIndex) Atom {
2723 assert(atom_index < self.atoms.items.len);
2724 return self.atoms.items[atom_index];
2725 }
2726
2727 fn getSegmentByName(self: Zld, segname: []const u8) ?u8 {
2728 for (self.segments.items, 0..) |seg, i| {
2729 if (mem.eql(u8, segname, seg.segName())) return @as(u8, @intCast(i));
2730 } else return null;
2731 }
2732
2733 pub fn getSegment(self: Zld, sect_id: u8) macho.segment_command_64 {
2734 const index = self.sections.items(.segment_index)[sect_id];
2735 return self.segments.items[index];
2736 }
2737
2738 pub fn getSegmentPtr(self: *Zld, sect_id: u8) *macho.segment_command_64 {
2739 const index = self.sections.items(.segment_index)[sect_id];
2740 return &self.segments.items[index];
2741 }
2742
2743 pub fn getLinkeditSegmentPtr(self: *Zld) *macho.segment_command_64 {
2744 assert(self.segments.items.len > 0);
2745 const seg = &self.segments.items[self.segments.items.len - 1];
2746 assert(mem.eql(u8, seg.segName(), "__LINKEDIT"));
2747 return seg;
2748 }
2749
2750 pub fn getSectionByName(self: Zld, segname: []const u8, sectname: []const u8) ?u8 {
2751 // TODO investigate caching with a hashmap
2752 for (self.sections.items(.header), 0..) |header, i| {
2753 if (mem.eql(u8, header.segName(), segname) and mem.eql(u8, header.sectName(), sectname))
2754 return @as(u8, @intCast(i));
2755 } else return null;
2756 }
2757
2758 pub fn getSectionIndexes(self: Zld, segment_index: u8) struct { start: u8, end: u8 } {
2759 var start: u8 = 0;
2760 const nsects = for (self.segments.items, 0..) |seg, i| {
2761 if (i == segment_index) break @as(u8, @intCast(seg.nsects));
2762 start += @as(u8, @intCast(seg.nsects));
2763 } else 0;
2764 return .{ .start = start, .end = start + nsects };
2765 }
2766
2767 pub fn symbolIsTemp(self: *Zld, sym_with_loc: SymbolWithLoc) bool {
2768 const sym = self.getSymbol(sym_with_loc);
2769 if (!sym.sect()) return false;
2770 if (sym.ext()) return false;
2771 const sym_name = self.getSymbolName(sym_with_loc);
2772 return mem.startsWith(u8, sym_name, "l") or mem.startsWith(u8, sym_name, "L");
2773 }
2774
2775 /// Returns pointer-to-symbol described by `sym_with_loc` descriptor.
2776 pub fn getSymbolPtr(self: *Zld, sym_with_loc: SymbolWithLoc) *macho.nlist_64 {
2777 if (sym_with_loc.getFile()) |file| {
2778 const object = &self.objects.items[file];
2779 return &object.symtab[sym_with_loc.sym_index];
2780 } else {
2781 return &self.locals.items[sym_with_loc.sym_index];
193 // libc++ dep
194 if (options.link_libcpp) {
195 try positionals.ensureUnusedCapacity(2);
196 positionals.appendAssumeCapacity(.{ .path = comp.libcxxabi_static_lib.?.full_object_path });
197 positionals.appendAssumeCapacity(.{ .path = comp.libcxx_static_lib.?.full_object_path });
2782198 }
2783 }
2784199
2785 /// Returns symbol described by `sym_with_loc` descriptor.
2786 pub fn getSymbol(self: *const Zld, sym_with_loc: SymbolWithLoc) macho.nlist_64 {
2787 if (sym_with_loc.getFile()) |file| {
2788 const object = &self.objects.items[file];
2789 return object.symtab[sym_with_loc.sym_index];
2790 } else {
2791 return self.locals.items[sym_with_loc.sym_index];
2792 }
2793 }
200 var libs = std.StringArrayHashMap(link.SystemLib).init(arena);
2794201
2795 /// Returns name of the symbol described by `sym_with_loc` descriptor.
2796 pub fn getSymbolName(self: *const Zld, sym_with_loc: SymbolWithLoc) []const u8 {
2797 if (sym_with_loc.getFile()) |file| {
2798 const object = self.objects.items[file];
2799 return object.getSymbolName(sym_with_loc.sym_index);
2800 } else {
2801 const sym = self.locals.items[sym_with_loc.sym_index];
2802 return self.strtab.get(sym.n_strx).?;
202 {
203 const vals = options.system_libs.values();
204 try libs.ensureUnusedCapacity(vals.len);
205 for (vals) |v| libs.putAssumeCapacity(v.path.?, v);
2803206 }
2804 }
2805
2806 /// Returns GOT atom that references `sym_with_loc` if one exists.
2807 /// Returns null otherwise.
2808 pub fn getGotAtomIndexForSymbol(self: *Zld, sym_with_loc: SymbolWithLoc) ?AtomIndex {
2809 const index = self.got_table.get(sym_with_loc) orelse return null;
2810 const entry = self.got_entries.items[index];
2811 return entry.atom_index;
2812 }
2813207
2814 /// Returns stubs atom that references `sym_with_loc` if one exists.
2815 /// Returns null otherwise.
2816 pub fn getStubsAtomIndexForSymbol(self: *Zld, sym_with_loc: SymbolWithLoc) ?AtomIndex {
2817 const index = self.stubs_table.get(sym_with_loc) orelse return null;
2818 const entry = self.stubs.items[index];
2819 return entry.atom_index;
2820 }
2821
2822 /// Returns TLV pointer atom that references `sym_with_loc` if one exists.
2823 /// Returns null otherwise.
2824 pub fn getTlvPtrAtomIndexForSymbol(self: *Zld, sym_with_loc: SymbolWithLoc) ?AtomIndex {
2825 const index = self.tlv_ptr_table.get(sym_with_loc) orelse return null;
2826 const entry = self.tlv_ptr_entries.items[index];
2827 return entry.atom_index;
2828 }
2829
2830 /// Returns symbol location corresponding to the set entrypoint.
2831 /// Asserts output mode is executable.
2832 pub fn getEntryPoint(self: Zld) SymbolWithLoc {
2833 assert(self.options.output_mode == .Exe);
2834 const global_index = self.entry_index.?;
2835 return self.globals.items[global_index];
2836 }
2837
2838 inline fn requiresThunks(self: Zld) bool {
2839 return self.options.target.cpu.arch == .aarch64;
2840 }
208 {
209 try libs.ensureUnusedCapacity(options.frameworks.len);
210 for (options.frameworks) |v| libs.putAssumeCapacity(v.path, .{
211 .needed = v.needed,
212 .weak = v.weak,
213 .path = v.path,
214 });
215 }
2841216
2842 pub fn generateSymbolStabs(self: *Zld, object: Object, locals: *std.ArrayList(macho.nlist_64)) !void {
2843 log.debug("generating stabs for '{s}'", .{object.name});
217 try MachO.resolveLibSystem(arena, comp, options.sysroot, target, options.lib_dirs, &libs);
2844218
2845 const gpa = self.gpa;
2846 var debug_info = object.parseDwarfInfo();
219 if (options.verbose_link) {
220 var argv = std.ArrayList([]const u8).init(arena);
2847221
2848 var lookup = DwarfInfo.AbbrevLookupTable.init(gpa);
2849 defer lookup.deinit();
2850 try lookup.ensureUnusedCapacity(std.math.maxInt(u8));
222 try argv.append("zig");
223 try argv.append("ld");
2851224
2852 // We assume there is only one CU.
2853 var cu_it = debug_info.getCompileUnitIterator();
2854 const compile_unit = while (try cu_it.next()) |cu| {
2855 const offset = math.cast(usize, cu.cuh.debug_abbrev_offset) orelse return error.Overflow;
2856 try debug_info.genAbbrevLookupByKind(offset, &lookup);
2857 break cu;
2858 } else {
2859 log.debug("no compile unit found in debug info in {s}; skipping", .{object.name});
2860 return;
2861 };
225 if (is_exe_or_dyn_lib) {
226 try argv.append("-dynamic");
227 }
2862228
2863 var abbrev_it = compile_unit.getAbbrevEntryIterator(debug_info);
2864 const cu_entry: DwarfInfo.AbbrevEntry = while (try abbrev_it.next(lookup)) |entry| switch (entry.tag) {
2865 dwarf.TAG.compile_unit => break entry,
2866 else => continue,
2867 } else {
2868 log.debug("missing DWARF_TAG_compile_unit tag in {s}; skipping", .{object.name});
2869 return;
2870 };
229 if (is_dyn_lib) {
230 try argv.append("-dylib");
2871231
2872 var maybe_tu_name: ?[]const u8 = null;
2873 var maybe_tu_comp_dir: ?[]const u8 = null;
2874 var attr_it = cu_entry.getAttributeIterator(debug_info, compile_unit.cuh);
232 if (options.install_name) |install_name| {
233 try argv.append("-install_name");
234 try argv.append(install_name);
235 }
236 }
2875237
2876 while (try attr_it.next()) |attr| switch (attr.name) {
2877 dwarf.AT.comp_dir => maybe_tu_comp_dir = attr.getString(debug_info, compile_unit.cuh) orelse continue,
2878 dwarf.AT.name => maybe_tu_name = attr.getString(debug_info, compile_unit.cuh) orelse continue,
2879 else => continue,
2880 };
238 if (options.sysroot) |syslibroot| {
239 try argv.append("-syslibroot");
240 try argv.append(syslibroot);
241 }
2881242
2882 if (maybe_tu_name == null or maybe_tu_comp_dir == null) {
2883 log.debug("missing DWARF_AT_comp_dir and DWARF_AT_name attributes {s}; skipping", .{object.name});
2884 return;
2885 }
243 for (options.rpath_list) |rpath| {
244 try argv.append("-rpath");
245 try argv.append(rpath);
246 }
2886247
2887 const tu_name = maybe_tu_name.?;
2888 const tu_comp_dir = maybe_tu_comp_dir.?;
248 if (options.pagezero_size) |pagezero_size| {
249 try argv.append("-pagezero_size");
250 try argv.append(try std.fmt.allocPrint(arena, "0x{x}", .{pagezero_size}));
251 }
2889252
2890 // Open scope
2891 try locals.ensureUnusedCapacity(3);
2892 locals.appendAssumeCapacity(.{
2893 .n_strx = try self.strtab.insert(gpa, tu_comp_dir),
2894 .n_type = macho.N_SO,
2895 .n_sect = 0,
2896 .n_desc = 0,
2897 .n_value = 0,
2898 });
2899 locals.appendAssumeCapacity(.{
2900 .n_strx = try self.strtab.insert(gpa, tu_name),
2901 .n_type = macho.N_SO,
2902 .n_sect = 0,
2903 .n_desc = 0,
2904 .n_value = 0,
2905 });
2906 locals.appendAssumeCapacity(.{
2907 .n_strx = try self.strtab.insert(gpa, object.name),
2908 .n_type = macho.N_OSO,
2909 .n_sect = 0,
2910 .n_desc = 1,
2911 .n_value = object.mtime,
2912 });
253 if (options.headerpad_size) |headerpad_size| {
254 try argv.append("-headerpad_size");
255 try argv.append(try std.fmt.allocPrint(arena, "0x{x}", .{headerpad_size}));
256 }
2913257
2914 var stabs_buf: [4]macho.nlist_64 = undefined;
258 if (options.headerpad_max_install_names) {
259 try argv.append("-headerpad_max_install_names");
260 }
2915261
2916 var name_lookup: ?DwarfInfo.SubprogramLookupByName = if (object.header.flags & macho.MH_SUBSECTIONS_VIA_SYMBOLS == 0) blk: {
2917 var name_lookup = DwarfInfo.SubprogramLookupByName.init(gpa);
2918 errdefer name_lookup.deinit();
2919 try name_lookup.ensureUnusedCapacity(@as(u32, @intCast(object.atoms.items.len)));
2920 try debug_info.genSubprogramLookupByName(compile_unit, lookup, &name_lookup);
2921 break :blk name_lookup;
2922 } else null;
2923 defer if (name_lookup) |*nl| nl.deinit();
2924
2925 for (object.atoms.items) |atom_index| {
2926 const atom = self.getAtom(atom_index);
2927 const stabs = try self.generateSymbolStabsForSymbol(
2928 atom_index,
2929 atom.getSymbolWithLoc(),
2930 name_lookup,
2931 &stabs_buf,
2932 );
2933 try locals.appendSlice(stabs);
2934
2935 var it = Atom.getInnerSymbolsIterator(self, atom_index);
2936 while (it.next()) |sym_loc| {
2937 const contained_stabs = try self.generateSymbolStabsForSymbol(
2938 atom_index,
2939 sym_loc,
2940 name_lookup,
2941 &stabs_buf,
2942 );
2943 try locals.appendSlice(contained_stabs);
262 if (gc_sections) {
263 try argv.append("-dead_strip");
2944264 }
2945 }
2946265
2947 // Close scope
2948 try locals.append(.{
2949 .n_strx = 0,
2950 .n_type = macho.N_SO,
2951 .n_sect = 0,
2952 .n_desc = 0,
2953 .n_value = 0,
2954 });
2955 }
266 if (options.dead_strip_dylibs) {
267 try argv.append("-dead_strip_dylibs");
268 }
2956269
2957 fn generateSymbolStabsForSymbol(
2958 self: *Zld,
2959 atom_index: AtomIndex,
2960 sym_loc: SymbolWithLoc,
2961 lookup: ?DwarfInfo.SubprogramLookupByName,
2962 buf: *[4]macho.nlist_64,
2963 ) ![]const macho.nlist_64 {
2964 const gpa = self.gpa;
2965 const object = self.objects.items[sym_loc.getFile().?];
2966 const sym = self.getSymbol(sym_loc);
2967 const sym_name = self.getSymbolName(sym_loc);
2968 const header = self.sections.items(.header)[sym.n_sect - 1];
2969
2970 if (sym.n_strx == 0) return buf[0..0];
2971 if (self.symbolIsTemp(sym_loc)) return buf[0..0];
2972
2973 if (!header.isCode()) {
2974 // Since we are not dealing with machine code, it's either a global or a static depending
2975 // on the linkage scope.
2976 if (sym.sect() and sym.ext()) {
2977 // Global gets an N_GSYM stab type.
2978 buf[0] = .{
2979 .n_strx = try self.strtab.insert(gpa, sym_name),
2980 .n_type = macho.N_GSYM,
2981 .n_sect = sym.n_sect,
2982 .n_desc = 0,
2983 .n_value = 0,
2984 };
2985 } else {
2986 // Local static gets an N_STSYM stab type.
2987 buf[0] = .{
2988 .n_strx = try self.strtab.insert(gpa, sym_name),
2989 .n_type = macho.N_STSYM,
2990 .n_sect = sym.n_sect,
2991 .n_desc = 0,
2992 .n_value = sym.n_value,
2993 };
270 if (options.entry) |entry| {
271 try argv.append("-e");
272 try argv.append(entry);
2994273 }
2995 return buf[0..1];
2996 }
2997274
2998 const size: u64 = size: {
2999 if (object.header.flags & macho.MH_SUBSECTIONS_VIA_SYMBOLS != 0) {
3000 break :size self.getAtom(atom_index).size;
275 for (options.objects) |obj| {
276 if (obj.must_link) {
277 try argv.append("-force_load");
278 }
279 try argv.append(obj.path);
3001280 }
3002281
3003 // Since we don't have subsections to work with, we need to infer the size of each function
3004 // the slow way by scanning the debug info for matching symbol names and extracting
3005 // the symbol's DWARF_AT_low_pc and DWARF_AT_high_pc values.
3006 const source_sym = object.getSourceSymbol(sym_loc.sym_index) orelse return buf[0..0];
3007 const subprogram = lookup.?.get(sym_name[1..]) orelse return buf[0..0];
282 for (comp.c_object_table.keys()) |key| {
283 try argv.append(key.status.success.object_path);
284 }
3008285
3009 if (subprogram.addr <= source_sym.n_value and source_sym.n_value < subprogram.addr + subprogram.size) {
3010 break :size subprogram.size;
3011 } else {
3012 log.debug("no stab found for {s}", .{sym_name});
3013 return buf[0..0];
286 if (module_obj_path) |p| {
287 try argv.append(p);
3014288 }
3015 };
3016289
3017 buf[0] = .{
3018 .n_strx = 0,
3019 .n_type = macho.N_BNSYM,
3020 .n_sect = sym.n_sect,
3021 .n_desc = 0,
3022 .n_value = sym.n_value,
3023 };
3024 buf[1] = .{
3025 .n_strx = try self.strtab.insert(gpa, sym_name),
3026 .n_type = macho.N_FUN,
3027 .n_sect = sym.n_sect,
3028 .n_desc = 0,
3029 .n_value = sym.n_value,
3030 };
3031 buf[2] = .{
3032 .n_strx = 0,
3033 .n_type = macho.N_FUN,
3034 .n_sect = 0,
3035 .n_desc = 0,
3036 .n_value = size,
3037 };
3038 buf[3] = .{
3039 .n_strx = 0,
3040 .n_type = macho.N_ENSYM,
3041 .n_sect = sym.n_sect,
3042 .n_desc = 0,
3043 .n_value = size,
3044 };
290 if (comp.compiler_rt_lib) |lib| {
291 try argv.append(lib.full_object_path);
292 }
3045293
3046 return buf;
3047 }
294 if (options.link_libcpp) {
295 try argv.append(comp.libcxxabi_static_lib.?.full_object_path);
296 try argv.append(comp.libcxx_static_lib.?.full_object_path);
297 }
3048298
3049 fn logSegments(self: *Zld) void {
3050 log.debug("segments:", .{});
3051 for (self.segments.items, 0..) |segment, i| {
3052 log.debug(" segment({d}): {s} @{x} ({x}), sizeof({x})", .{
3053 i,
3054 segment.segName(),
3055 segment.fileoff,
3056 segment.vmaddr,
3057 segment.vmsize,
3058 });
3059 }
3060 }
299 try argv.append("-o");
300 try argv.append(full_out_path);
3061301
3062 fn logSections(self: *Zld) void {
3063 log.debug("sections:", .{});
3064 for (self.sections.items(.header), 0..) |header, i| {
3065 log.debug(" sect({d}): {s},{s} @{x} ({x}), sizeof({x})", .{
3066 i + 1,
3067 header.segName(),
3068 header.sectName(),
3069 header.offset,
3070 header.addr,
3071 header.size,
3072 });
3073 }
3074 }
302 try argv.append("-lSystem");
303 try argv.append("-lc");
3075304
3076 fn logSymAttributes(sym: macho.nlist_64, buf: []u8) []const u8 {
3077 if (sym.sect()) {
3078 buf[0] = 's';
3079 }
3080 if (sym.ext()) {
3081 if (sym.weakDef() or sym.pext()) {
3082 buf[1] = 'w';
3083 } else {
3084 buf[1] = 'e';
305 for (options.system_libs.keys()) |l_name| {
306 const info = options.system_libs.get(l_name).?;
307 const arg = if (info.needed)
308 try std.fmt.allocPrint(arena, "-needed-l{s}", .{l_name})
309 else if (info.weak)
310 try std.fmt.allocPrint(arena, "-weak-l{s}", .{l_name})
311 else
312 try std.fmt.allocPrint(arena, "-l{s}", .{l_name});
313 try argv.append(arg);
3085314 }
3086 }
3087 if (sym.tentative()) {
3088 buf[2] = 't';
3089 }
3090 if (sym.undf()) {
3091 buf[3] = 'u';
3092 }
3093 return buf[0..];
3094 }
3095315
3096 fn logSymtab(self: *Zld) void {
3097 var buf: [4]u8 = undefined;
316 for (options.lib_dirs) |lib_dir| {
317 try argv.append(try std.fmt.allocPrint(arena, "-L{s}", .{lib_dir}));
318 }
3098319
3099 const scoped_log = std.log.scoped(.symtab);
320 for (options.frameworks) |framework| {
321 const name = std.fs.path.stem(framework.path);
322 const arg = if (framework.needed)
323 try std.fmt.allocPrint(arena, "-needed_framework {s}", .{name})
324 else if (framework.weak)
325 try std.fmt.allocPrint(arena, "-weak_framework {s}", .{name})
326 else
327 try std.fmt.allocPrint(arena, "-framework {s}", .{name});
328 try argv.append(arg);
329 }
3100330
3101 scoped_log.debug("locals:", .{});
3102 for (self.objects.items, 0..) |object, id| {
3103 scoped_log.debug(" object({d}): {s}", .{ id, object.name });
3104 if (object.in_symtab == null) continue;
3105 for (object.symtab, 0..) |sym, sym_id| {
3106 @memset(&buf, '_');
3107 scoped_log.debug(" %{d}: {s} @{x} in sect({d}), {s}", .{
3108 sym_id,
3109 object.getSymbolName(@as(u32, @intCast(sym_id))),
3110 sym.n_value,
3111 sym.n_sect,
3112 logSymAttributes(sym, &buf),
3113 });
331 for (options.framework_dirs) |framework_dir| {
332 try argv.append(try std.fmt.allocPrint(arena, "-F{s}", .{framework_dir}));
3114333 }
3115 }
3116 scoped_log.debug(" object(-1)", .{});
3117 for (self.locals.items, 0..) |sym, sym_id| {
3118 if (sym.undf()) continue;
3119 scoped_log.debug(" %{d}: {s} @{x} in sect({d}), {s}", .{
3120 sym_id,
3121 self.strtab.get(sym.n_strx).?,
3122 sym.n_value,
3123 sym.n_sect,
3124 logSymAttributes(sym, &buf),
3125 });
3126 }
3127334
3128 scoped_log.debug("exports:", .{});
3129 for (self.globals.items, 0..) |global, i| {
3130 const sym = self.getSymbol(global);
3131 if (sym.undf()) continue;
3132 if (sym.n_desc == N_DEAD) continue;
3133 scoped_log.debug(" %{d}: {s} @{x} in sect({d}), {s} (def in object({?}))", .{
3134 i,
3135 self.getSymbolName(global),
3136 sym.n_value,
3137 sym.n_sect,
3138 logSymAttributes(sym, &buf),
3139 global.file,
3140 });
3141 }
335 if (is_dyn_lib and (options.allow_shlib_undefined orelse false)) {
336 try argv.append("-undefined");
337 try argv.append("dynamic_lookup");
338 }
3142339
3143 scoped_log.debug("imports:", .{});
3144 for (self.globals.items, 0..) |global, i| {
3145 const sym = self.getSymbol(global);
3146 if (!sym.undf()) continue;
3147 if (sym.n_desc == N_DEAD) continue;
3148 const ord = @divTrunc(sym.n_desc, macho.N_SYMBOL_RESOLVER);
3149 scoped_log.debug(" %{d}: {s} @{x} in ord({d}), {s}", .{
3150 i,
3151 self.getSymbolName(global),
3152 sym.n_value,
3153 ord,
3154 logSymAttributes(sym, &buf),
3155 });
340 Compilation.dump_argv(argv.items);
3156341 }
3157342
3158 scoped_log.debug("GOT entries:", .{});
3159 for (self.got_entries.items, 0..) |entry, i| {
3160 const atom_sym = entry.getAtomSymbol(self);
3161 const target_sym = entry.getTargetSymbol(self);
3162 const target_sym_name = entry.getTargetSymbolName(self);
3163 if (target_sym.undf()) {
3164 scoped_log.debug(" {d}@{x} => import('{s}')", .{
3165 i,
3166 atom_sym.n_value,
3167 target_sym_name,
3168 });
3169 } else {
3170 scoped_log.debug(" {d}@{x} => local(%{d}) in object({?}) {s}", .{
3171 i,
3172 atom_sym.n_value,
3173 entry.target.sym_index,
3174 entry.target.file,
3175 logSymAttributes(target_sym, buf[0..4]),
3176 });
3177 }
3178 }
343 var dependent_libs = std.fifo.LinearFifo(MachO.DylibReExportInfo, .Dynamic).init(arena);
3179344
3180 scoped_log.debug("__thread_ptrs entries:", .{});
3181 for (self.tlv_ptr_entries.items, 0..) |entry, i| {
3182 const atom_sym = entry.getAtomSymbol(self);
3183 const target_sym = entry.getTargetSymbol(self);
3184 const target_sym_name = entry.getTargetSymbolName(self);
3185 assert(target_sym.undf());
3186 scoped_log.debug(" {d}@{x} => import('{s}')", .{
3187 i,
3188 atom_sym.n_value,
3189 target_sym_name,
3190 });
3191 }
345 for (positionals.items) |obj| {
346 const in_file = try std.fs.cwd().openFile(obj.path, .{});
347 defer in_file.close();
3192348
3193 scoped_log.debug("stubs entries:", .{});
3194 for (self.stubs.items, 0..) |entry, i| {
3195 const atom_sym = entry.getAtomSymbol(self);
3196 const target_sym = entry.getTargetSymbol(self);
3197 const target_sym_name = entry.getTargetSymbolName(self);
3198 assert(target_sym.undf());
3199 scoped_log.debug(" {d}@{x} => import('{s}')", .{
3200 i,
3201 atom_sym.n_value,
3202 target_sym_name,
3203 });
3204 }
349 var parse_ctx = MachO.ParseErrorCtx.init(gpa);
350 defer parse_ctx.deinit();
3205351
3206 scoped_log.debug("thunks:", .{});
3207 for (self.thunks.items, 0..) |thunk, i| {
3208 scoped_log.debug(" thunk({d})", .{i});
3209 for (thunk.lookup.keys(), 0..) |target, j| {
3210 const target_sym = self.getSymbol(target);
3211 const atom = self.getAtom(thunk.lookup.get(target).?);
3212 const atom_sym = self.getSymbol(atom.getSymbolWithLoc());
3213 scoped_log.debug(" {d}@{x} => thunk('{s}'@{x})", .{
3214 j,
3215 atom_sym.n_value,
3216 self.getSymbolName(target),
3217 target_sym.n_value,
3218 });
3219 }
352 macho_file.parsePositional(
353 in_file,
354 obj.path,
355 obj.must_link,
356 &dependent_libs,
357 &parse_ctx,
358 ) catch |err| try macho_file.handleAndReportParseError(obj.path, err, &parse_ctx);
3220359 }
3221 }
3222360
3223 fn logAtoms(self: *Zld) void {
3224 log.debug("atoms:", .{});
3225 const slice = self.sections.slice();
3226 for (slice.items(.first_atom_index), 0..) |first_atom_index, sect_id| {
3227 var atom_index = first_atom_index;
3228 if (atom_index == 0) continue;
361 for (libs.keys(), libs.values()) |path, lib| {
362 const in_file = try std.fs.cwd().openFile(path, .{});
363 defer in_file.close();
3229364
3230 const header = slice.items(.header)[sect_id];
365 var parse_ctx = MachO.ParseErrorCtx.init(gpa);
366 defer parse_ctx.deinit();
3231367
3232 log.debug("{s},{s}", .{ header.segName(), header.sectName() });
368 macho_file.parseLibrary(
369 in_file,
370 path,
371 lib,
372 false,
373 false,
374 null,
375 &dependent_libs,
376 &parse_ctx,
377 ) catch |err| try macho_file.handleAndReportParseError(path, err, &parse_ctx);
378 }
3233379
3234 while (true) {
3235 const atom = self.getAtom(atom_index);
3236 self.logAtom(atom_index, log);
380 try macho_file.parseDependentLibs(&dependent_libs);
3237381
3238 if (atom.next_index) |next_index| {
3239 atom_index = next_index;
3240 } else break;
3241 }
382 var actions = std.ArrayList(MachO.ResolveAction).init(gpa);
383 defer actions.deinit();
384 try macho_file.resolveSymbols(&actions);
385 if (macho_file.unresolved.count() > 0) {
386 try macho_file.reportUndefined();
387 return error.FlushFailure;
3242388 }
3243 }
3244389
3245 pub fn logAtom(self: *Zld, atom_index: AtomIndex, logger: anytype) void {
3246 if (!build_options.enable_logging) return;
3247
3248 const atom = self.getAtom(atom_index);
3249 const sym = self.getSymbol(atom.getSymbolWithLoc());
3250 const sym_name = self.getSymbolName(atom.getSymbolWithLoc());
3251 logger.debug(" ATOM({d}, %{d}, '{s}') @ {x} (sizeof({x}), alignof({x})) in object({?}) in sect({d})", .{
3252 atom_index,
3253 atom.sym_index,
3254 sym_name,
3255 sym.n_value,
3256 atom.size,
3257 atom.alignment,
3258 atom.getFile(),
3259 sym.n_sect,
3260 });
390 for (macho_file.objects.items, 0..) |*object, object_id| {
391 object.splitIntoAtoms(macho_file, @as(u32, @intCast(object_id))) catch |err| switch (err) {
392 error.MissingEhFrameSection => try macho_file.reportParseError(
393 object.name,
394 "missing section: '__TEXT,__eh_frame' is required but could not be found",
395 .{},
396 ),
397 error.BadDwarfCfi => try macho_file.reportParseError(
398 object.name,
399 "invalid DWARF: failed to parse '__TEXT,__eh_frame' section",
400 .{},
401 ),
402 else => |e| return e,
403 };
404 }
3261405
3262 if (atom.getFile() != null) {
3263 var it = Atom.getInnerSymbolsIterator(self, atom_index);
3264 while (it.next()) |sym_loc| {
3265 const inner = self.getSymbol(sym_loc);
3266 const inner_name = self.getSymbolName(sym_loc);
3267 const offset = Atom.calcInnerSymbolOffset(self, atom_index, sym_loc.sym_index);
3268
3269 logger.debug(" (%{d}, '{s}') @ {x} ({x})", .{
3270 sym_loc.sym_index,
3271 inner_name,
3272 inner.n_value,
3273 offset,
3274 });
3275 }
406 if (gc_sections) {
407 try dead_strip.gcAtoms(macho_file);
408 }
3276409
3277 if (Atom.getSectionAlias(self, atom_index)) |sym_loc| {
3278 const alias = self.getSymbol(sym_loc);
3279 const alias_name = self.getSymbolName(sym_loc);
410 try macho_file.createDyldPrivateAtom();
411 try macho_file.createTentativeDefAtoms();
3280412
3281 logger.debug(" (%{d}, '{s}') @ {x} ({x})", .{
3282 sym_loc.sym_index,
3283 alias_name,
3284 alias.n_value,
3285 0,
3286 });
413 if (macho_file.base.options.output_mode == .Exe) {
414 const global = macho_file.getEntryPoint().?;
415 if (macho_file.getSymbol(global).undf()) {
416 // We do one additional check here in case the entry point was found in one of the dylibs.
417 // (I actually have no idea what this would imply but it is a possible outcome and so we
418 // support it.)
419 try macho_file.addStubEntry(global);
3287420 }
3288421 }
3289 }
3290};
3291
3292pub const N_DEAD: u16 = @as(u16, @bitCast(@as(i16, -1)));
3293422
3294const Section = struct {
3295 header: macho.section_64,
3296 segment_index: u8,
3297 first_atom_index: AtomIndex,
3298 last_atom_index: AtomIndex,
3299};
3300
3301pub const AtomIndex = u32;
3302
3303const IndirectPointer = struct {
3304 target: SymbolWithLoc,
3305 atom_index: AtomIndex,
3306
3307 pub fn getTargetSymbol(self: @This(), zld: *Zld) macho.nlist_64 {
3308 return zld.getSymbol(self.target);
3309 }
423 for (macho_file.objects.items) |object| {
424 for (object.atoms.items) |atom_index| {
425 const atom = macho_file.getAtom(atom_index);
426 const sym = macho_file.getSymbol(atom.getSymbolWithLoc());
427 const header = macho_file.sections.items(.header)[sym.n_sect - 1];
428 if (header.isZerofill()) continue;
3310429
3311 pub fn getTargetSymbolName(self: @This(), zld: *Zld) []const u8 {
3312 return zld.getSymbolName(self.target);
3313 }
430 const relocs = Atom.getAtomRelocs(macho_file, atom_index);
431 try Atom.scanAtomRelocs(macho_file, atom_index, relocs);
432 }
433 }
3314434
3315 pub fn getAtomSymbol(self: @This(), zld: *Zld) macho.nlist_64 {
3316 const atom = zld.getAtom(self.atom_index);
3317 return zld.getSymbol(atom.getSymbolWithLoc());
3318 }
3319};
435 try eh_frame.scanRelocs(macho_file);
436 try UnwindInfo.scanRelocs(macho_file);
3320437
3321pub const SymbolWithLoc = extern struct {
3322 // Index into the respective symbol table.
3323 sym_index: u32,
438 if (macho_file.dyld_stub_binder_index) |index|
439 try macho_file.addGotEntry(macho_file.globals.items[index]);
3324440
3325 // 0 means it's a synthetic global.
3326 file: u32 = 0,
441 try calcSectionSizes(macho_file);
3327442
3328 pub fn getFile(self: SymbolWithLoc) ?u32 {
3329 if (self.file == 0) return null;
3330 return self.file - 1;
3331 }
443 var unwind_info = UnwindInfo{ .gpa = gpa };
444 defer unwind_info.deinit();
445 try unwind_info.collect(macho_file);
3332446
3333 pub fn eql(self: SymbolWithLoc, other: SymbolWithLoc) bool {
3334 return self.file == other.file and self.sym_index == other.sym_index;
3335 }
3336};
447 try eh_frame.calcSectionSize(macho_file, &unwind_info);
448 unwind_info.calcSectionSize(macho_file);
3337449
3338pub const SymbolResolver = struct {
3339 arena: Allocator,
3340 table: std.StringHashMap(u32),
3341 unresolved: std.AutoArrayHashMap(u32, void),
3342};
450 try pruneAndSortSections(macho_file);
451 try createSegments(macho_file);
452 try allocateSegments(macho_file);
3343453
3344pub fn linkWithZld(macho_file: *MachO, comp: *Compilation, prog_node: *std.Progress.Node) link.File.FlushError!void {
3345 const tracy = trace(@src());
3346 defer tracy.end();
454 try macho_file.allocateSpecialSymbols();
3347455
3348 const gpa = macho_file.base.allocator;
3349 const options = &macho_file.base.options;
3350 const target = options.target;
456 if (build_options.enable_logging) {
457 macho_file.logSymtab();
458 macho_file.logSegments();
459 macho_file.logSections();
460 macho_file.logAtoms();
461 }
3351462
3352 var arena_allocator = std.heap.ArenaAllocator.init(gpa);
3353 defer arena_allocator.deinit();
3354 const arena = arena_allocator.allocator();
463 try writeAtoms(macho_file);
464 if (macho_file.base.options.target.cpu.arch == .aarch64) try writeThunks(macho_file);
465 try writeDyldPrivateAtom(macho_file);
3355466
3356 const directory = options.emit.?.directory; // Just an alias to make it shorter to type.
3357 const full_out_path = try directory.join(arena, &[_][]const u8{options.emit.?.sub_path});
467 if (macho_file.stubs_section_index) |_| {
468 try writeStubs(macho_file);
469 try writeStubHelpers(macho_file);
470 try writeLaSymbolPtrs(macho_file);
471 }
472 if (macho_file.got_section_index) |sect_id|
473 try writePointerEntries(macho_file, sect_id, &macho_file.got_table);
474 if (macho_file.tlv_ptr_section_index) |sect_id|
475 try writePointerEntries(macho_file, sect_id, &macho_file.tlv_ptr_table);
3358476
3359 // If there is no Zig code to compile, then we should skip flushing the output file because it
3360 // will not be part of the linker line anyway.
3361 const module_obj_path: ?[]const u8 = if (options.module != null) blk: {
3362 try macho_file.flushModule(comp, prog_node);
477 try eh_frame.write(macho_file, &unwind_info);
478 try unwind_info.write(macho_file);
479 try macho_file.writeLinkeditSegmentData();
3363480
3364 if (fs.path.dirname(full_out_path)) |dirname| {
3365 break :blk try fs.path.join(arena, &.{ dirname, macho_file.base.intermediary_basename.? });
3366 } else {
3367 break :blk macho_file.base.intermediary_basename.?;
481 // If the last section of __DATA segment is zerofill section, we need to ensure
482 // that the free space between the end of the last non-zerofill section of __DATA
483 // segment and the beginning of __LINKEDIT segment is zerofilled as the loader will
484 // copy-paste this space into memory for quicker zerofill operation.
485 if (macho_file.data_segment_cmd_index) |data_seg_id| blk: {
486 var physical_zerofill_start: ?u64 = null;
487 const section_indexes = macho_file.getSectionIndexes(data_seg_id);
488 for (macho_file.sections.items(.header)[section_indexes.start..section_indexes.end]) |header| {
489 if (header.isZerofill() and header.size > 0) break;
490 physical_zerofill_start = header.offset + header.size;
491 } else break :blk;
492 const start = physical_zerofill_start orelse break :blk;
493 const linkedit = macho_file.getLinkeditSegmentPtr();
494 const size = math.cast(usize, linkedit.fileoff - start) orelse return error.Overflow;
495 if (size > 0) {
496 log.debug("zeroing out zerofill area of length {x} at {x}", .{ size, start });
497 var padding = try gpa.alloc(u8, size);
498 defer gpa.free(padding);
499 @memset(padding, 0);
500 try macho_file.base.file.?.pwriteAll(padding, start);
501 }
3368502 }
3369 } else null;
3370503
3371 var sub_prog_node = prog_node.start("MachO Flush", 0);
3372 sub_prog_node.activate();
3373 sub_prog_node.context.refresh();
3374 defer sub_prog_node.end();
504 // Write code signature padding if required
505 var codesig: ?CodeSignature = if (MachO.requiresCodeSignature(&macho_file.base.options)) blk: {
506 // Preallocate space for the code signature.
507 // We need to do this at this stage so that we have the load commands with proper values
508 // written out to the file.
509 // The most important here is to have the correct vm and filesize of the __LINKEDIT segment
510 // where the code signature goes into.
511 var codesig = CodeSignature.init(MachO.getPageSize(cpu_arch));
512 codesig.code_directory.ident = fs.path.basename(full_out_path);
513 if (options.entitlements) |path| {
514 try codesig.addEntitlements(gpa, path);
515 }
516 try macho_file.writeCodeSignaturePadding(&codesig);
517 break :blk codesig;
518 } else null;
519 defer if (codesig) |*csig| csig.deinit(gpa);
3375520
3376 const cpu_arch = target.cpu.arch;
3377 const os_tag = target.os.tag;
3378 const abi = target.abi;
3379 const is_lib = options.output_mode == .Lib;
3380 const is_dyn_lib = options.link_mode == .Dynamic and is_lib;
3381 const is_exe_or_dyn_lib = is_dyn_lib or options.output_mode == .Exe;
3382 const stack_size = options.stack_size_override orelse 0;
3383 const is_debug_build = options.optimize_mode == .Debug;
3384 const gc_sections = options.gc_sections orelse !is_debug_build;
521 // Write load commands
522 var lc_buffer = std.ArrayList(u8).init(arena);
523 const lc_writer = lc_buffer.writer();
3385524
3386 const id_symlink_basename = "zld.id";
525 try macho_file.writeSegmentHeaders(lc_writer);
526 try lc_writer.writeStruct(macho_file.dyld_info_cmd);
527 try lc_writer.writeStruct(macho_file.function_starts_cmd);
528 try lc_writer.writeStruct(macho_file.data_in_code_cmd);
529 try lc_writer.writeStruct(macho_file.symtab_cmd);
530 try lc_writer.writeStruct(macho_file.dysymtab_cmd);
531 try load_commands.writeDylinkerLC(lc_writer);
3387532
3388 var man: Cache.Manifest = undefined;
3389 defer if (!options.disable_lld_caching) man.deinit();
533 switch (macho_file.base.options.output_mode) {
534 .Exe => blk: {
535 const seg_id = macho_file.header_segment_cmd_index.?;
536 const seg = macho_file.segments.items[seg_id];
537 const global = macho_file.getEntryPoint() orelse break :blk;
538 const sym = macho_file.getSymbol(global);
539
540 const addr: u64 = if (sym.undf())
541 // In this case, the symbol has been resolved in one of dylibs and so we point
542 // to the stub as its vmaddr value.
543 macho_file.getStubsEntryAddress(global).?
544 else
545 sym.n_value;
3390546
3391 var digest: [Cache.hex_digest_len]u8 = undefined;
547 try lc_writer.writeStruct(macho.entry_point_command{
548 .entryoff = @as(u32, @intCast(addr - seg.vmaddr)),
549 .stacksize = macho_file.base.options.stack_size_override orelse 0,
550 });
551 },
552 .Lib => if (macho_file.base.options.link_mode == .Dynamic) {
553 try load_commands.writeDylibIdLC(gpa, &macho_file.base.options, lc_writer);
554 },
555 else => {},
556 }
3392557
3393 if (!options.disable_lld_caching) {
3394 man = comp.cache_parent.obtain();
558 try load_commands.writeRpathLCs(gpa, &macho_file.base.options, lc_writer);
559 try lc_writer.writeStruct(macho.source_version_command{
560 .version = 0,
561 });
562 {
563 const platform = Platform.fromTarget(macho_file.base.options.target);
564 const sdk_version: ?std.SemanticVersion = if (macho_file.base.options.sysroot) |path|
565 load_commands.inferSdkVersionFromSdkPath(path)
566 else
567 null;
568 if (platform.isBuildVersionCompatible()) {
569 try load_commands.writeBuildVersionLC(platform, sdk_version, lc_writer);
570 } else {
571 try load_commands.writeVersionMinLC(platform, sdk_version, lc_writer);
572 }
573 }
3395574
3396 // We are about to obtain this lock, so here we give other processes a chance first.
3397 macho_file.base.releaseLock();
575 const uuid_cmd_offset = @sizeOf(macho.mach_header_64) + @as(u32, @intCast(lc_buffer.items.len));
576 try lc_writer.writeStruct(macho_file.uuid_cmd);
3398577
3399 comptime assert(Compilation.link_hash_implementation_version == 10);
578 try load_commands.writeLoadDylibLCs(
579 macho_file.dylibs.items,
580 macho_file.referenced_dylibs.keys(),
581 lc_writer,
582 );
3400583
3401 for (options.objects) |obj| {
3402 _ = try man.addFile(obj.path, null);
3403 man.hash.add(obj.must_link);
3404 }
3405 for (comp.c_object_table.keys()) |key| {
3406 _ = try man.addFile(key.status.success.object_path, null);
3407 }
3408 try man.addOptionalFile(module_obj_path);
3409 // We can skip hashing libc and libc++ components that we are in charge of building from Zig
3410 // installation sources because they are always a product of the compiler version + target information.
3411 man.hash.add(stack_size);
3412 man.hash.addOptional(options.pagezero_size);
3413 man.hash.addOptional(options.headerpad_size);
3414 man.hash.add(options.headerpad_max_install_names);
3415 man.hash.add(gc_sections);
3416 man.hash.add(options.dead_strip_dylibs);
3417 man.hash.add(options.strip);
3418 man.hash.addListOfBytes(options.lib_dirs);
3419 man.hash.addListOfBytes(options.framework_dirs);
3420 try link.hashAddFrameworks(&man, options.frameworks);
3421 man.hash.addListOfBytes(options.rpath_list);
3422 if (is_dyn_lib) {
3423 man.hash.addOptionalBytes(options.install_name);
3424 man.hash.addOptional(options.version);
584 if (codesig != null) {
585 try lc_writer.writeStruct(macho_file.codesig_cmd);
3425586 }
3426 try link.hashAddSystemLibs(&man, options.system_libs);
3427 man.hash.addOptionalBytes(options.sysroot);
3428 man.hash.addListOfBytes(options.force_undefined_symbols.keys());
3429 try man.addOptionalFile(options.entitlements);
3430587
3431 // We don't actually care whether it's a cache hit or miss; we just
3432 // need the digest and the lock.
3433 _ = try man.hit();
3434 digest = man.final();
588 const ncmds = load_commands.calcNumOfLCs(lc_buffer.items);
589 try macho_file.base.file.?.pwriteAll(lc_buffer.items, @sizeOf(macho.mach_header_64));
590 try macho_file.writeHeader(ncmds, @as(u32, @intCast(lc_buffer.items.len)));
591 try macho_file.writeUuid(comp, uuid_cmd_offset, codesig != null);
3435592
3436 var prev_digest_buf: [digest.len]u8 = undefined;
3437 const prev_digest: []u8 = Cache.readSmallFile(
3438 directory.handle,
3439 id_symlink_basename,
3440 &prev_digest_buf,
3441 ) catch |err| blk: {
3442 log.debug("MachO Zld new_digest={s} error: {s}", .{
3443 std.fmt.fmtSliceHexLower(&digest),
3444 @errorName(err),
3445 });
3446 // Handle this as a cache miss.
3447 break :blk prev_digest_buf[0..0];
3448 };
3449 if (mem.eql(u8, prev_digest, &digest)) {
3450 // Hot diggity dog! The output binary is already there.
3451 log.debug("MachO Zld digest={s} match - skipping invocation", .{
3452 std.fmt.fmtSliceHexLower(&digest),
3453 });
3454 macho_file.base.lock = man.toOwnedLock();
3455 return;
593 if (codesig) |*csig| {
594 try macho_file.writeCodeSignature(comp, csig); // code signing always comes last
595 try MachO.invalidateKernelCache(directory.handle, macho_file.base.options.emit.?.sub_path);
3456596 }
3457 log.debug("MachO Zld prev_digest={s} new_digest={s}", .{
3458 std.fmt.fmtSliceHexLower(prev_digest),
3459 std.fmt.fmtSliceHexLower(&digest),
3460 });
597 }
3461598
3462 // We are about to change the output file to be different, so we invalidate the build hash now.
3463 directory.handle.deleteFile(id_symlink_basename) catch |err| switch (err) {
3464 error.FileNotFound => {},
3465 else => |e| return e,
599 if (!options.disable_lld_caching) {
600 // Update the file with the digest. If it fails we can continue; it only
601 // means that the next invocation will have an unnecessary cache miss.
602 Cache.writeSmallFile(directory.handle, id_symlink_basename, &digest) catch |err| {
603 log.debug("failed to save linking hash digest file: {s}", .{@errorName(err)});
3466604 };
605 // Again failure here only means an unnecessary cache miss.
606 if (man.have_exclusive_lock) {
607 man.writeManifest() catch |err| {
608 log.debug("failed to write cache manifest when linking: {s}", .{@errorName(err)});
609 };
610 }
611 // We hang on to this lock so that the output file path can be used without
612 // other processes clobbering it.
613 macho_file.base.lock = man.toOwnedLock();
3467614 }
615}
3468616
3469 if (options.output_mode == .Obj) {
3470 // LLD's MachO driver does not support the equivalent of `-r` so we do a simple file copy
3471 // here. TODO: think carefully about how we can avoid this redundant operation when doing
3472 // build-obj. See also the corresponding TODO in linkAsArchive.
3473 const the_object_path = blk: {
3474 if (options.objects.len != 0) {
3475 break :blk options.objects[0].path;
3476 }
617fn createSegments(macho_file: *MachO) !void {
618 const gpa = macho_file.base.allocator;
619 const pagezero_vmsize = macho_file.base.options.pagezero_size orelse MachO.default_pagezero_vmsize;
620 const page_size = MachO.getPageSize(macho_file.base.options.target.cpu.arch);
621 const aligned_pagezero_vmsize = mem.alignBackward(u64, pagezero_vmsize, page_size);
622 if (macho_file.base.options.output_mode != .Lib and aligned_pagezero_vmsize > 0) {
623 if (aligned_pagezero_vmsize != pagezero_vmsize) {
624 log.warn("requested __PAGEZERO size (0x{x}) is not page aligned", .{pagezero_vmsize});
625 log.warn(" rounding down to 0x{x}", .{aligned_pagezero_vmsize});
626 }
627 macho_file.pagezero_segment_cmd_index = @intCast(macho_file.segments.items.len);
628 try macho_file.segments.append(gpa, .{
629 .cmdsize = @sizeOf(macho.segment_command_64),
630 .segname = MachO.makeStaticString("__PAGEZERO"),
631 .vmsize = aligned_pagezero_vmsize,
632 });
633 }
3477634
3478 if (comp.c_object_table.count() != 0)
3479 break :blk comp.c_object_table.keys()[0].status.success.object_path;
635 // __TEXT segment is non-optional
636 {
637 const protection = MachO.getSegmentMemoryProtection("__TEXT");
638 macho_file.text_segment_cmd_index = @intCast(macho_file.segments.items.len);
639 macho_file.header_segment_cmd_index = macho_file.text_segment_cmd_index.?;
640 try macho_file.segments.append(gpa, .{
641 .cmdsize = @sizeOf(macho.segment_command_64),
642 .segname = MachO.makeStaticString("__TEXT"),
643 .maxprot = protection,
644 .initprot = protection,
645 });
646 }
3480647
3481 if (module_obj_path) |p|
3482 break :blk p;
648 for (macho_file.sections.items(.header), 0..) |header, sect_id| {
649 if (header.size == 0) continue; // empty section
3483650
3484 // TODO I think this is unreachable. Audit this situation when solving the above TODO
3485 // regarding eliding redundant object -> object transformations.
3486 return error.NoObjectsToLink;
651 const segname = header.segName();
652 const segment_id = macho_file.getSegmentByName(segname) orelse blk: {
653 log.debug("creating segment '{s}'", .{segname});
654 const segment_id = @as(u8, @intCast(macho_file.segments.items.len));
655 const protection = MachO.getSegmentMemoryProtection(segname);
656 try macho_file.segments.append(gpa, .{
657 .cmdsize = @sizeOf(macho.segment_command_64),
658 .segname = MachO.makeStaticString(segname),
659 .maxprot = protection,
660 .initprot = protection,
661 });
662 break :blk segment_id;
3487663 };
3488 // This can happen when using --enable-cache and using the stage1 backend. In this case
3489 // we can skip the file copy.
3490 if (!mem.eql(u8, the_object_path, full_out_path)) {
3491 try fs.cwd().copyFile(the_object_path, fs.cwd(), full_out_path, .{});
3492 }
3493 } else {
3494 const page_size = macho_file.page_size;
3495 const sub_path = options.emit.?.sub_path;
664 const segment = &macho_file.segments.items[segment_id];
665 segment.cmdsize += @sizeOf(macho.section_64);
666 segment.nsects += 1;
667 macho_file.sections.items(.segment_index)[sect_id] = segment_id;
668 }
3496669
3497 const file = try directory.handle.createFile(sub_path, .{
3498 .truncate = true,
3499 .read = true,
3500 .mode = link.determineMode(options.*),
3501 });
3502 defer file.close();
670 if (macho_file.getSegmentByName("__DATA_CONST")) |index| {
671 macho_file.data_const_segment_cmd_index = index;
672 }
3503673
3504 var zld = Zld{
3505 .gpa = gpa,
3506 .file = file,
3507 .page_size = macho_file.page_size,
3508 .options = options,
3509 };
3510 defer zld.deinit();
674 if (macho_file.getSegmentByName("__DATA")) |index| {
675 macho_file.data_segment_cmd_index = index;
676 }
3511677
3512 try zld.atoms.append(gpa, Atom.empty); // AtomIndex at 0 is reserved as null atom
3513 try zld.strtab.buffer.append(gpa, 0);
678 // __LINKEDIT always comes last
679 {
680 const protection = MachO.getSegmentMemoryProtection("__LINKEDIT");
681 macho_file.linkedit_segment_cmd_index = @intCast(macho_file.segments.items.len);
682 try macho_file.segments.append(gpa, .{
683 .cmdsize = @sizeOf(macho.segment_command_64),
684 .segname = MachO.makeStaticString("__LINKEDIT"),
685 .maxprot = protection,
686 .initprot = protection,
687 });
688 }
689}
3514690
3515 // Positional arguments to the linker such as object files and static archives.
3516 var positionals = std.ArrayList([]const u8).init(arena);
3517 try positionals.ensureUnusedCapacity(options.objects.len);
691fn writeAtoms(macho_file: *MachO) !void {
692 const gpa = macho_file.base.allocator;
693 const slice = macho_file.sections.slice();
3518694
3519 var must_link_archives = std.StringArrayHashMap(void).init(arena);
3520 try must_link_archives.ensureUnusedCapacity(options.objects.len);
695 for (slice.items(.first_atom_index), 0..) |first_atom_index, sect_id| {
696 const header = slice.items(.header)[sect_id];
697 if (header.isZerofill()) continue;
3521698
3522 for (options.objects) |obj| {
3523 if (must_link_archives.contains(obj.path)) continue;
3524 if (obj.must_link) {
3525 _ = must_link_archives.getOrPutAssumeCapacity(obj.path);
3526 } else {
3527 _ = positionals.appendAssumeCapacity(obj.path);
3528 }
3529 }
699 var atom_index = first_atom_index orelse continue;
3530700
3531 for (comp.c_object_table.keys()) |key| {
3532 try positionals.append(key.status.success.object_path);
3533 }
701 var buffer = try gpa.alloc(u8, math.cast(usize, header.size) orelse return error.Overflow);
702 defer gpa.free(buffer);
703 @memset(buffer, 0); // TODO with NOPs
3534704
3535 if (module_obj_path) |p| {
3536 try positionals.append(p);
3537 }
705 log.debug("writing atoms in {s},{s}", .{ header.segName(), header.sectName() });
3538706
3539 if (comp.compiler_rt_lib) |lib| {
3540 try positionals.append(lib.full_object_path);
3541 }
707 while (true) {
708 const atom = macho_file.getAtom(atom_index);
709 if (atom.getFile()) |file| {
710 const this_sym = macho_file.getSymbol(atom.getSymbolWithLoc());
711 const padding_size: usize = if (atom.next_index) |next_index| blk: {
712 const next_sym = macho_file.getSymbol(macho_file.getAtom(next_index).getSymbolWithLoc());
713 const size = next_sym.n_value - (this_sym.n_value + atom.size);
714 break :blk math.cast(usize, size) orelse return error.Overflow;
715 } else 0;
3542716
3543 // libc++ dep
3544 if (options.link_libcpp) {
3545 try positionals.append(comp.libcxxabi_static_lib.?.full_object_path);
3546 try positionals.append(comp.libcxx_static_lib.?.full_object_path);
3547 }
717 log.debug(" (adding ATOM(%{d}, '{s}') from object({d}) to buffer)", .{
718 atom.sym_index,
719 macho_file.getSymbolName(atom.getSymbolWithLoc()),
720 file,
721 });
722 if (padding_size > 0) {
723 log.debug(" (with padding {x})", .{padding_size});
724 }
3548725
3549 var libs = std.StringArrayHashMap(link.SystemLib).init(arena);
726 const offset = math.cast(usize, this_sym.n_value - header.addr) orelse
727 return error.Overflow;
728 log.debug(" (at offset 0x{x})", .{offset});
729
730 const code = Atom.getAtomCode(macho_file, atom_index);
731 const relocs = Atom.getAtomRelocs(macho_file, atom_index);
732 const size = math.cast(usize, atom.size) orelse return error.Overflow;
733 @memcpy(buffer[offset .. offset + size], code);
734 try Atom.resolveRelocs(
735 macho_file,
736 atom_index,
737 buffer[offset..][0..size],
738 relocs,
739 );
740 }
3550741
3551 {
3552 const vals = options.system_libs.values();
3553 try libs.ensureUnusedCapacity(vals.len);
3554 for (vals) |v| libs.putAssumeCapacity(v.path.?, v);
742 if (atom.next_index) |next_index| {
743 atom_index = next_index;
744 } else break;
3555745 }
3556746
3557 {
3558 try libs.ensureUnusedCapacity(options.frameworks.len);
3559 for (options.frameworks) |v| libs.putAssumeCapacity(v.path, .{
3560 .needed = v.needed,
3561 .weak = v.weak,
3562 .path = v.path,
3563 });
3564 }
747 log.debug(" (writing at file offset 0x{x})", .{header.offset});
748 try macho_file.base.file.?.pwriteAll(buffer, header.offset);
749 }
750}
3565751
3566 try MachO.resolveLibSystem(arena, comp, options.sysroot, target, options.lib_dirs, &libs);
752fn writeDyldPrivateAtom(macho_file: *MachO) !void {
753 const atom_index = macho_file.dyld_private_atom_index orelse return;
754 const atom = macho_file.getAtom(atom_index);
755 const sym = macho_file.getSymbol(atom.getSymbolWithLoc());
756 const sect_id = macho_file.data_section_index.?;
757 const header = macho_file.sections.items(.header)[sect_id];
758 const offset = sym.n_value - header.addr + header.offset;
759 log.debug("writing __dyld_private at offset 0x{x}", .{offset});
760 const buffer: [@sizeOf(u64)]u8 = [_]u8{0} ** @sizeOf(u64);
761 try macho_file.base.file.?.pwriteAll(&buffer, offset);
762}
3567763
3568 if (options.verbose_link) {
3569 var argv = std.ArrayList([]const u8).init(arena);
764fn writeThunks(macho_file: *MachO) !void {
765 assert(macho_file.base.options.target.cpu.arch == .aarch64);
766 const gpa = macho_file.base.allocator;
3570767
3571 try argv.append("zig");
3572 try argv.append("ld");
768 const sect_id = macho_file.text_section_index orelse return;
769 const header = macho_file.sections.items(.header)[sect_id];
3573770
3574 if (is_exe_or_dyn_lib) {
3575 try argv.append("-dynamic");
3576 }
771 for (macho_file.thunks.items, 0..) |*thunk, i| {
772 if (thunk.getSize() == 0) continue;
773 const thunk_size = math.cast(usize, thunk.getSize()) orelse return error.Overflow;
774 var buffer = try std.ArrayList(u8).initCapacity(gpa, thunk_size);
775 defer buffer.deinit();
776 try thunks.writeThunkCode(macho_file, thunk, buffer.writer());
777 const thunk_atom = macho_file.getAtom(thunk.getStartAtomIndex());
778 const thunk_sym = macho_file.getSymbol(thunk_atom.getSymbolWithLoc());
779 const offset = thunk_sym.n_value - header.addr + header.offset;
780 log.debug("writing thunk({d}) at offset 0x{x}", .{ i, offset });
781 try macho_file.base.file.?.pwriteAll(buffer.items, offset);
782 }
783}
3577784
3578 if (is_dyn_lib) {
3579 try argv.append("-dylib");
785fn writePointerEntries(macho_file: *MachO, sect_id: u8, table: anytype) !void {
786 const gpa = macho_file.base.allocator;
787 const header = macho_file.sections.items(.header)[sect_id];
788 const capacity = math.cast(usize, header.size) orelse return error.Overflow;
789 var buffer = try std.ArrayList(u8).initCapacity(gpa, capacity);
790 defer buffer.deinit();
791 for (table.entries.items) |entry| {
792 const sym = macho_file.getSymbol(entry);
793 buffer.writer().writeIntLittle(u64, sym.n_value) catch unreachable;
794 }
795 log.debug("writing __DATA_CONST,__got contents at file offset 0x{x}", .{header.offset});
796 try macho_file.base.file.?.pwriteAll(buffer.items, header.offset);
797}
3580798
3581 if (options.install_name) |install_name| {
3582 try argv.append("-install_name");
3583 try argv.append(install_name);
3584 }
3585 }
799fn writeStubs(macho_file: *MachO) !void {
800 const gpa = macho_file.base.allocator;
801 const cpu_arch = macho_file.base.options.target.cpu.arch;
802 const stubs_header = macho_file.sections.items(.header)[macho_file.stubs_section_index.?];
803 const la_symbol_ptr_header = macho_file.sections.items(.header)[macho_file.la_symbol_ptr_section_index.?];
3586804
3587 if (options.sysroot) |syslibroot| {
3588 try argv.append("-syslibroot");
3589 try argv.append(syslibroot);
3590 }
805 const capacity = math.cast(usize, stubs_header.size) orelse return error.Overflow;
806 var buffer = try std.ArrayList(u8).initCapacity(gpa, capacity);
807 defer buffer.deinit();
3591808
3592 for (options.rpath_list) |rpath| {
3593 try argv.append("-rpath");
3594 try argv.append(rpath);
3595 }
809 for (0..macho_file.stub_table.count()) |index| {
810 try stubs.writeStubCode(.{
811 .cpu_arch = cpu_arch,
812 .source_addr = stubs_header.addr + stubs.stubSize(cpu_arch) * index,
813 .target_addr = la_symbol_ptr_header.addr + index * @sizeOf(u64),
814 }, buffer.writer());
815 }
3596816
3597 if (options.pagezero_size) |pagezero_size| {
3598 try argv.append("-pagezero_size");
3599 try argv.append(try std.fmt.allocPrint(arena, "0x{x}", .{pagezero_size}));
3600 }
817 log.debug("writing __TEXT,__stubs contents at file offset 0x{x}", .{stubs_header.offset});
818 try macho_file.base.file.?.pwriteAll(buffer.items, stubs_header.offset);
819}
3601820
3602 if (options.headerpad_size) |headerpad_size| {
3603 try argv.append("-headerpad_size");
3604 try argv.append(try std.fmt.allocPrint(arena, "0x{x}", .{headerpad_size}));
3605 }
821fn writeStubHelpers(macho_file: *MachO) !void {
822 const gpa = macho_file.base.allocator;
823 const cpu_arch = macho_file.base.options.target.cpu.arch;
824 const stub_helper_header = macho_file.sections.items(.header)[macho_file.stub_helper_section_index.?];
3606825
3607 if (options.headerpad_max_install_names) {
3608 try argv.append("-headerpad_max_install_names");
3609 }
826 const capacity = math.cast(usize, stub_helper_header.size) orelse return error.Overflow;
827 var buffer = try std.ArrayList(u8).initCapacity(gpa, capacity);
828 defer buffer.deinit();
3610829
3611 if (gc_sections) {
3612 try argv.append("-dead_strip");
3613 }
830 {
831 const dyld_private_addr = blk: {
832 const atom = macho_file.getAtom(macho_file.dyld_private_atom_index.?);
833 const sym = macho_file.getSymbol(atom.getSymbolWithLoc());
834 break :blk sym.n_value;
835 };
836 const dyld_stub_binder_got_addr = blk: {
837 const sym_loc = macho_file.globals.items[macho_file.dyld_stub_binder_index.?];
838 break :blk macho_file.getGotEntryAddress(sym_loc).?;
839 };
840 try stubs.writeStubHelperPreambleCode(.{
841 .cpu_arch = cpu_arch,
842 .source_addr = stub_helper_header.addr,
843 .dyld_private_addr = dyld_private_addr,
844 .dyld_stub_binder_got_addr = dyld_stub_binder_got_addr,
845 }, buffer.writer());
846 }
3614847
3615 if (options.dead_strip_dylibs) {
3616 try argv.append("-dead_strip_dylibs");
3617 }
848 for (0..macho_file.stub_table.count()) |index| {
849 const source_addr = stub_helper_header.addr + stubs.stubHelperPreambleSize(cpu_arch) +
850 stubs.stubHelperSize(cpu_arch) * index;
851 try stubs.writeStubHelperCode(.{
852 .cpu_arch = cpu_arch,
853 .source_addr = source_addr,
854 .target_addr = stub_helper_header.addr,
855 }, buffer.writer());
856 }
3618857
3619 if (options.entry) |entry| {
3620 try argv.append("-e");
3621 try argv.append(entry);
3622 }
858 log.debug("writing __TEXT,__stub_helper contents at file offset 0x{x}", .{
859 stub_helper_header.offset,
860 });
861 try macho_file.base.file.?.pwriteAll(buffer.items, stub_helper_header.offset);
862}
3623863
3624 for (options.objects) |obj| {
3625 try argv.append(obj.path);
3626 }
864fn writeLaSymbolPtrs(macho_file: *MachO) !void {
865 const gpa = macho_file.base.allocator;
866 const cpu_arch = macho_file.base.options.target.cpu.arch;
867 const la_symbol_ptr_header = macho_file.sections.items(.header)[macho_file.la_symbol_ptr_section_index.?];
868 const stub_helper_header = macho_file.sections.items(.header)[macho_file.stub_helper_section_index.?];
3627869
3628 for (comp.c_object_table.keys()) |key| {
3629 try argv.append(key.status.success.object_path);
3630 }
870 const capacity = math.cast(usize, la_symbol_ptr_header.size) orelse return error.Overflow;
871 var buffer = try std.ArrayList(u8).initCapacity(gpa, capacity);
872 defer buffer.deinit();
3631873
3632 if (module_obj_path) |p| {
3633 try argv.append(p);
3634 }
874 for (0..macho_file.stub_table.count()) |index| {
875 const target_addr = stub_helper_header.addr + stubs.stubHelperPreambleSize(cpu_arch) +
876 stubs.stubHelperSize(cpu_arch) * index;
877 buffer.writer().writeIntLittle(u64, target_addr) catch unreachable;
878 }
3635879
3636 if (comp.compiler_rt_lib) |lib| {
3637 try argv.append(lib.full_object_path);
3638 }
880 log.debug("writing __DATA,__la_symbol_ptr contents at file offset 0x{x}", .{
881 la_symbol_ptr_header.offset,
882 });
883 try macho_file.base.file.?.pwriteAll(buffer.items, la_symbol_ptr_header.offset);
884}
3639885
3640 if (options.link_libcpp) {
3641 try argv.append(comp.libcxxabi_static_lib.?.full_object_path);
3642 try argv.append(comp.libcxx_static_lib.?.full_object_path);
3643 }
886fn pruneAndSortSections(macho_file: *MachO) !void {
887 const Entry = struct {
888 index: u8,
3644889
3645 try argv.append("-o");
3646 try argv.append(full_out_path);
890 pub fn lessThan(ctx: *MachO, lhs: @This(), rhs: @This()) bool {
891 const lhs_header = ctx.sections.items(.header)[lhs.index];
892 const rhs_header = ctx.sections.items(.header)[rhs.index];
893 return MachO.getSectionPrecedence(lhs_header) < MachO.getSectionPrecedence(rhs_header);
894 }
895 };
3647896
3648 try argv.append("-lSystem");
3649 try argv.append("-lc");
897 const gpa = macho_file.base.allocator;
3650898
3651 for (options.system_libs.keys()) |l_name| {
3652 const info = options.system_libs.get(l_name).?;
3653 const arg = if (info.needed)
3654 try std.fmt.allocPrint(arena, "-needed-l{s}", .{l_name})
3655 else if (info.weak)
3656 try std.fmt.allocPrint(arena, "-weak-l{s}", .{l_name})
3657 else
3658 try std.fmt.allocPrint(arena, "-l{s}", .{l_name});
3659 try argv.append(arg);
3660 }
899 var entries = try std.ArrayList(Entry).initCapacity(gpa, macho_file.sections.slice().len);
900 defer entries.deinit();
3661901
3662 for (options.lib_dirs) |lib_dir| {
3663 try argv.append(try std.fmt.allocPrint(arena, "-L{s}", .{lib_dir}));
902 for (0..macho_file.sections.slice().len) |index| {
903 const section = macho_file.sections.get(index);
904 if (section.header.size == 0) {
905 log.debug("pruning section {s},{s} {?d}", .{
906 section.header.segName(),
907 section.header.sectName(),
908 section.first_atom_index,
909 });
910 for (&[_]*?u8{
911 &macho_file.text_section_index,
912 &macho_file.data_const_section_index,
913 &macho_file.data_section_index,
914 &macho_file.bss_section_index,
915 &macho_file.thread_vars_section_index,
916 &macho_file.thread_data_section_index,
917 &macho_file.thread_bss_section_index,
918 &macho_file.eh_frame_section_index,
919 &macho_file.unwind_info_section_index,
920 &macho_file.got_section_index,
921 &macho_file.tlv_ptr_section_index,
922 &macho_file.stubs_section_index,
923 &macho_file.stub_helper_section_index,
924 &macho_file.la_symbol_ptr_section_index,
925 }) |maybe_index| {
926 if (maybe_index.* != null and maybe_index.*.? == index) {
927 maybe_index.* = null;
928 }
3664929 }
930 continue;
931 }
932 entries.appendAssumeCapacity(.{ .index = @intCast(index) });
933 }
3665934
3666 for (options.frameworks) |framework| {
3667 const name = std.fs.path.stem(framework.path);
3668 const arg = if (framework.needed)
3669 try std.fmt.allocPrint(arena, "-needed_framework {s}", .{name})
3670 else if (framework.weak)
3671 try std.fmt.allocPrint(arena, "-weak_framework {s}", .{name})
3672 else
3673 try std.fmt.allocPrint(arena, "-framework {s}", .{name});
3674 try argv.append(arg);
3675 }
935 mem.sort(Entry, entries.items, macho_file, Entry.lessThan);
3676936
3677 for (options.framework_dirs) |framework_dir| {
3678 try argv.append(try std.fmt.allocPrint(arena, "-F{s}", .{framework_dir}));
3679 }
937 var slice = macho_file.sections.toOwnedSlice();
938 defer slice.deinit(gpa);
3680939
3681 if (is_dyn_lib and (options.allow_shlib_undefined orelse false)) {
3682 try argv.append("-undefined");
3683 try argv.append("dynamic_lookup");
3684 }
940 const backlinks = try gpa.alloc(u8, slice.len);
941 defer gpa.free(backlinks);
942 for (entries.items, 0..) |entry, i| {
943 backlinks[entry.index] = @as(u8, @intCast(i));
944 }
3685945
3686 for (must_link_archives.keys()) |lib| {
3687 try argv.append(try std.fmt.allocPrint(arena, "-force_load {s}", .{lib}));
3688 }
946 try macho_file.sections.ensureTotalCapacity(gpa, entries.items.len);
947 for (entries.items) |entry| {
948 macho_file.sections.appendAssumeCapacity(slice.get(entry.index));
949 }
3689950
3690 Compilation.dump_argv(argv.items);
951 for (&[_]*?u8{
952 &macho_file.text_section_index,
953 &macho_file.data_const_section_index,
954 &macho_file.data_section_index,
955 &macho_file.bss_section_index,
956 &macho_file.thread_vars_section_index,
957 &macho_file.thread_data_section_index,
958 &macho_file.thread_bss_section_index,
959 &macho_file.eh_frame_section_index,
960 &macho_file.unwind_info_section_index,
961 &macho_file.got_section_index,
962 &macho_file.tlv_ptr_section_index,
963 &macho_file.stubs_section_index,
964 &macho_file.stub_helper_section_index,
965 &macho_file.la_symbol_ptr_section_index,
966 }) |maybe_index| {
967 if (maybe_index.*) |*index| {
968 index.* = backlinks[index.*];
3691969 }
970 }
971}
3692972
3693 var dependent_libs = std.fifo.LinearFifo(struct {
3694 id: Dylib.Id,
3695 parent: u16,
3696 }, .Dynamic).init(arena);
973fn calcSectionSizes(macho_file: *MachO) !void {
974 const slice = macho_file.sections.slice();
975 for (slice.items(.header), 0..) |*header, sect_id| {
976 if (header.size == 0) continue;
977 if (macho_file.text_section_index) |txt| {
978 if (txt == sect_id and macho_file.base.options.target.cpu.arch == .aarch64) continue;
979 }
3697980
3698 try zld.parseInputFiles(positionals.items, options.sysroot, &dependent_libs);
3699 try zld.parseAndForceLoadStaticArchives(must_link_archives.keys());
3700 try zld.parseLibs(libs.keys(), libs.values(), options.sysroot, &dependent_libs);
3701 try zld.parseDependentLibs(options.sysroot, &dependent_libs);
981 var atom_index = slice.items(.first_atom_index)[sect_id] orelse continue;
3702982
3703 var resolver = SymbolResolver{
3704 .arena = arena,
3705 .table = std.StringHashMap(u32).init(arena),
3706 .unresolved = std.AutoArrayHashMap(u32, void).init(arena),
3707 };
3708 try zld.resolveSymbols(&resolver);
983 header.size = 0;
984 header.@"align" = 0;
3709985
3710 if (resolver.unresolved.count() > 0) {
3711 return error.UndefinedSymbolReference;
3712 }
986 while (true) {
987 const atom = macho_file.getAtom(atom_index);
988 const atom_alignment = try math.powi(u32, 2, atom.alignment);
989 const atom_offset = mem.alignForward(u64, header.size, atom_alignment);
990 const padding = atom_offset - header.size;
3713991
3714 if (options.output_mode == .Exe) {
3715 const entry_name = options.entry orelse load_commands.default_entry_point;
3716 const global_index = resolver.table.get(entry_name).?; // Error was flagged earlier
3717 zld.entry_index = global_index;
3718 }
992 const sym = macho_file.getSymbolPtr(atom.getSymbolWithLoc());
993 sym.n_value = atom_offset;
3719994
3720 for (zld.objects.items, 0..) |*object, object_id| {
3721 try object.splitIntoAtoms(&zld, @as(u32, @intCast(object_id)));
3722 }
995 header.size += padding + atom.size;
996 header.@"align" = @max(header.@"align", atom.alignment);
3723997
3724 if (gc_sections) {
3725 try dead_strip.gcAtoms(&zld, &resolver);
998 if (atom.next_index) |next_index| {
999 atom_index = next_index;
1000 } else break;
37261001 }
1002 }
37271003
3728 try zld.createDyldPrivateAtom();
3729 try zld.createTentativeDefAtoms();
3730 try zld.createStubHelperPreambleAtom();
1004 if (macho_file.text_section_index != null and macho_file.base.options.target.cpu.arch == .aarch64) {
1005 // Create jump/branch range extenders if needed.
1006 try thunks.createThunks(macho_file, macho_file.text_section_index.?);
1007 }
37311008
3732 if (zld.options.output_mode == .Exe) {
3733 const global = zld.getEntryPoint();
3734 if (zld.getSymbol(global).undf()) {
3735 // We do one additional check here in case the entry point was found in one of the dylibs.
3736 // (I actually have no idea what this would imply but it is a possible outcome and so we
3737 // support it.)
3738 try Atom.addStub(&zld, global);
3739 }
3740 }
1009 // Update offsets of all symbols contained within each Atom.
1010 // We need to do this since our unwind info synthesiser relies on
1011 // traversing the symbols when synthesising unwind info and DWARF CFI records.
1012 for (slice.items(.first_atom_index)) |first_atom_index| {
1013 var atom_index = first_atom_index orelse continue;
37411014
3742 for (zld.objects.items) |object| {
3743 for (object.atoms.items) |atom_index| {
3744 const atom = zld.getAtom(atom_index);
3745 const sym = zld.getSymbol(atom.getSymbolWithLoc());
3746 const header = zld.sections.items(.header)[sym.n_sect - 1];
3747 if (header.isZerofill()) continue;
1015 while (true) {
1016 const atom = macho_file.getAtom(atom_index);
1017 const sym = macho_file.getSymbol(atom.getSymbolWithLoc());
1018
1019 if (atom.getFile() != null) {
1020 // Update each symbol contained within the atom
1021 var it = Atom.getInnerSymbolsIterator(macho_file, atom_index);
1022 while (it.next()) |sym_loc| {
1023 const inner_sym = macho_file.getSymbolPtr(sym_loc);
1024 inner_sym.n_value = sym.n_value + Atom.calcInnerSymbolOffset(
1025 macho_file,
1026 atom_index,
1027 sym_loc.sym_index,
1028 );
1029 }
37481030
3749 const relocs = Atom.getAtomRelocs(&zld, atom_index);
3750 try Atom.scanAtomRelocs(&zld, atom_index, relocs);
1031 // If there is a section alias, update it now too
1032 if (Atom.getSectionAlias(macho_file, atom_index)) |sym_loc| {
1033 const alias = macho_file.getSymbolPtr(sym_loc);
1034 alias.n_value = sym.n_value;
1035 }
37511036 }
1037
1038 if (atom.next_index) |next_index| {
1039 atom_index = next_index;
1040 } else break;
37521041 }
1042 }
1043
1044 if (macho_file.got_section_index) |sect_id| {
1045 const header = &macho_file.sections.items(.header)[sect_id];
1046 header.size = macho_file.got_table.count() * @sizeOf(u64);
1047 header.@"align" = 3;
1048 }
37531049
3754 try eh_frame.scanRelocs(&zld);
3755 try UnwindInfo.scanRelocs(&zld);
1050 if (macho_file.tlv_ptr_section_index) |sect_id| {
1051 const header = &macho_file.sections.items(.header)[sect_id];
1052 header.size = macho_file.tlv_ptr_table.count() * @sizeOf(u64);
1053 header.@"align" = 3;
1054 }
37561055
3757 try zld.createDyldStubBinderGotAtom();
1056 const cpu_arch = macho_file.base.options.target.cpu.arch;
37581057
3759 try zld.calcSectionSizes();
1058 if (macho_file.stubs_section_index) |sect_id| {
1059 const header = &macho_file.sections.items(.header)[sect_id];
1060 header.size = macho_file.stub_table.count() * stubs.stubSize(cpu_arch);
1061 header.@"align" = math.log2(stubs.stubAlignment(cpu_arch));
1062 }
37601063
3761 var unwind_info = UnwindInfo{ .gpa = zld.gpa };
3762 defer unwind_info.deinit();
3763 try unwind_info.collect(&zld);
1064 if (macho_file.stub_helper_section_index) |sect_id| {
1065 const header = &macho_file.sections.items(.header)[sect_id];
1066 header.size = macho_file.stub_table.count() * stubs.stubHelperSize(cpu_arch) +
1067 stubs.stubHelperPreambleSize(cpu_arch);
1068 header.@"align" = math.log2(stubs.stubAlignment(cpu_arch));
1069 }
37641070
3765 try eh_frame.calcSectionSize(&zld, &unwind_info);
3766 try unwind_info.calcSectionSize(&zld);
1071 if (macho_file.la_symbol_ptr_section_index) |sect_id| {
1072 const header = &macho_file.sections.items(.header)[sect_id];
1073 header.size = macho_file.stub_table.count() * @sizeOf(u64);
1074 header.@"align" = 3;
1075 }
1076}
37671077
3768 try zld.pruneAndSortSections();
3769 try zld.createSegments();
3770 try zld.allocateSegments();
1078fn allocateSegments(macho_file: *MachO) !void {
1079 const gpa = macho_file.base.allocator;
1080 for (macho_file.segments.items, 0..) |*segment, segment_index| {
1081 const is_text_segment = mem.eql(u8, segment.segName(), "__TEXT");
1082 const base_size = if (is_text_segment)
1083 try load_commands.calcMinHeaderPad(gpa, &macho_file.base.options, .{
1084 .segments = macho_file.segments.items,
1085 .dylibs = macho_file.dylibs.items,
1086 .referenced_dylibs = macho_file.referenced_dylibs.keys(),
1087 })
1088 else
1089 0;
1090 try allocateSegment(macho_file, @as(u8, @intCast(segment_index)), base_size);
1091 }
1092}
37711093
3772 try zld.allocateSpecialSymbols();
1094fn getSegmentAllocBase(macho_file: *MachO, segment_index: u8) struct { vmaddr: u64, fileoff: u64 } {
1095 if (segment_index > 0) {
1096 const prev_segment = macho_file.segments.items[segment_index - 1];
1097 return .{
1098 .vmaddr = prev_segment.vmaddr + prev_segment.vmsize,
1099 .fileoff = prev_segment.fileoff + prev_segment.filesize,
1100 };
1101 }
1102 return .{ .vmaddr = 0, .fileoff = 0 };
1103}
37731104
3774 if (build_options.enable_logging) {
3775 zld.logSymtab();
3776 zld.logSegments();
3777 zld.logSections();
3778 zld.logAtoms();
3779 }
1105fn allocateSegment(macho_file: *MachO, segment_index: u8, init_size: u64) !void {
1106 const segment = &macho_file.segments.items[segment_index];
37801107
3781 try zld.writeAtoms();
3782 try eh_frame.write(&zld, &unwind_info);
3783 try unwind_info.write(&zld);
3784 try zld.writeLinkeditSegmentData();
1108 if (mem.eql(u8, segment.segName(), "__PAGEZERO")) return; // allocated upon creation
37851109
3786 // If the last section of __DATA segment is zerofill section, we need to ensure
3787 // that the free space between the end of the last non-zerofill section of __DATA
3788 // segment and the beginning of __LINKEDIT segment is zerofilled as the loader will
3789 // copy-paste this space into memory for quicker zerofill operation.
3790 if (zld.getSegmentByName("__DATA")) |data_seg_id| blk: {
3791 var physical_zerofill_start: ?u64 = null;
3792 const section_indexes = zld.getSectionIndexes(data_seg_id);
3793 for (zld.sections.items(.header)[section_indexes.start..section_indexes.end]) |header| {
3794 if (header.isZerofill() and header.size > 0) break;
3795 physical_zerofill_start = header.offset + header.size;
3796 } else break :blk;
3797 const start = physical_zerofill_start orelse break :blk;
3798 const linkedit = zld.getLinkeditSegmentPtr();
3799 const size = math.cast(usize, linkedit.fileoff - start) orelse return error.Overflow;
3800 if (size > 0) {
3801 log.debug("zeroing out zerofill area of length {x} at {x}", .{ size, start });
3802 var padding = try zld.gpa.alloc(u8, size);
3803 defer zld.gpa.free(padding);
3804 @memset(padding, 0);
3805 try zld.file.pwriteAll(padding, start);
3806 }
3807 }
1110 const base = getSegmentAllocBase(macho_file, segment_index);
1111 segment.vmaddr = base.vmaddr;
1112 segment.fileoff = base.fileoff;
1113 segment.filesize = init_size;
1114 segment.vmsize = init_size;
38081115
3809 // Write code signature padding if required
3810 const requires_codesig = blk: {
3811 if (options.entitlements) |_| break :blk true;
3812 if (cpu_arch == .aarch64 and (os_tag == .macos or abi == .simulator)) break :blk true;
3813 break :blk false;
3814 };
3815 var codesig: ?CodeSignature = if (requires_codesig) blk: {
3816 // Preallocate space for the code signature.
3817 // We need to do this at this stage so that we have the load commands with proper values
3818 // written out to the file.
3819 // The most important here is to have the correct vm and filesize of the __LINKEDIT segment
3820 // where the code signature goes into.
3821 var codesig = CodeSignature.init(page_size);
3822 codesig.code_directory.ident = fs.path.basename(full_out_path);
3823 if (options.entitlements) |path| {
3824 try codesig.addEntitlements(zld.gpa, path);
3825 }
3826 try zld.writeCodeSignaturePadding(&codesig);
3827 break :blk codesig;
3828 } else null;
3829 defer if (codesig) |*csig| csig.deinit(zld.gpa);
1116 // Allocate the sections according to their alignment at the beginning of the segment.
1117 const indexes = macho_file.getSectionIndexes(segment_index);
1118 var start = init_size;
38301119
3831 // Write load commands
3832 var lc_buffer = std.ArrayList(u8).init(arena);
3833 const lc_writer = lc_buffer.writer();
1120 const slice = macho_file.sections.slice();
1121 for (slice.items(.header)[indexes.start..indexes.end], 0..) |*header, sect_id| {
1122 const alignment = try math.powi(u32, 2, header.@"align");
1123 const start_aligned = mem.alignForward(u64, start, alignment);
1124 const n_sect = @as(u8, @intCast(indexes.start + sect_id + 1));
38341125
3835 try zld.writeSegmentHeaders(lc_writer);
3836 try lc_writer.writeStruct(zld.dyld_info_cmd);
3837 try lc_writer.writeStruct(zld.function_starts_cmd);
3838 try lc_writer.writeStruct(zld.data_in_code_cmd);
3839 try lc_writer.writeStruct(zld.symtab_cmd);
3840 try lc_writer.writeStruct(zld.dysymtab_cmd);
3841 try load_commands.writeDylinkerLC(lc_writer);
1126 header.offset = if (header.isZerofill())
1127 0
1128 else
1129 @as(u32, @intCast(segment.fileoff + start_aligned));
1130 header.addr = segment.vmaddr + start_aligned;
1131
1132 if (slice.items(.first_atom_index)[indexes.start + sect_id]) |first_atom_index| {
1133 var atom_index = first_atom_index;
38421134
3843 if (zld.options.output_mode == .Exe) {
3844 const seg_id = zld.getSegmentByName("__TEXT").?;
3845 const seg = zld.segments.items[seg_id];
3846 const global = zld.getEntryPoint();
3847 const sym = zld.getSymbol(global);
3848
3849 const addr: u64 = if (sym.undf()) blk: {
3850 // In this case, the symbol has been resolved in one of dylibs and so we point
3851 // to the stub as its vmaddr value.
3852 const stub_atom_index = zld.getStubsAtomIndexForSymbol(global).?;
3853 const stub_atom = zld.getAtom(stub_atom_index);
3854 const stub_sym = zld.getSymbol(stub_atom.getSymbolWithLoc());
3855 break :blk stub_sym.n_value;
3856 } else sym.n_value;
3857
3858 try lc_writer.writeStruct(macho.entry_point_command{
3859 .entryoff = @as(u32, @intCast(addr - seg.vmaddr)),
3860 .stacksize = options.stack_size_override orelse 0,
1135 log.debug("allocating local symbols in sect({d}, '{s},{s}')", .{
1136 n_sect,
1137 header.segName(),
1138 header.sectName(),
38611139 });
3862 } else {
3863 assert(zld.options.output_mode == .Lib);
3864 try load_commands.writeDylibIdLC(zld.gpa, zld.options, lc_writer);
3865 }
38661140
3867 try load_commands.writeRpathLCs(zld.gpa, zld.options, lc_writer);
3868 try lc_writer.writeStruct(macho.source_version_command{
3869 .version = 0,
3870 });
3871 try load_commands.writeBuildVersionLC(zld.options, lc_writer);
1141 while (true) {
1142 const atom = macho_file.getAtom(atom_index);
1143 const sym = macho_file.getSymbolPtr(atom.getSymbolWithLoc());
1144 sym.n_value += header.addr;
1145 sym.n_sect = n_sect;
38721146
3873 const uuid_cmd_offset = @sizeOf(macho.mach_header_64) + @as(u32, @intCast(lc_buffer.items.len));
3874 try lc_writer.writeStruct(zld.uuid_cmd);
1147 log.debug(" ATOM(%{d}, '{s}') @{x}", .{
1148 atom.sym_index,
1149 macho_file.getSymbolName(atom.getSymbolWithLoc()),
1150 sym.n_value,
1151 });
1152
1153 if (atom.getFile() != null) {
1154 // Update each symbol contained within the atom
1155 var it = Atom.getInnerSymbolsIterator(macho_file, atom_index);
1156 while (it.next()) |sym_loc| {
1157 const inner_sym = macho_file.getSymbolPtr(sym_loc);
1158 inner_sym.n_value = sym.n_value + Atom.calcInnerSymbolOffset(
1159 macho_file,
1160 atom_index,
1161 sym_loc.sym_index,
1162 );
1163 inner_sym.n_sect = n_sect;
1164 }
38751165
3876 try load_commands.writeLoadDylibLCs(zld.dylibs.items, zld.referenced_dylibs.keys(), lc_writer);
1166 // If there is a section alias, update it now too
1167 if (Atom.getSectionAlias(macho_file, atom_index)) |sym_loc| {
1168 const alias = macho_file.getSymbolPtr(sym_loc);
1169 alias.n_value = sym.n_value;
1170 alias.n_sect = n_sect;
1171 }
1172 }
38771173
3878 if (requires_codesig) {
3879 try lc_writer.writeStruct(zld.codesig_cmd);
1174 if (atom.next_index) |next_index| {
1175 atom_index = next_index;
1176 } else break;
1177 }
38801178 }
38811179
3882 const ncmds = load_commands.calcNumOfLCs(lc_buffer.items);
3883 try zld.file.pwriteAll(lc_buffer.items, @sizeOf(macho.mach_header_64));
3884 try zld.writeHeader(ncmds, @as(u32, @intCast(lc_buffer.items.len)));
3885 try zld.writeUuid(comp, uuid_cmd_offset, requires_codesig);
1180 start = start_aligned + header.size;
38861181
3887 if (codesig) |*csig| {
3888 try zld.writeCodeSignature(comp, csig); // code signing always comes last
3889 try MachO.invalidateKernelCache(directory.handle, zld.options.emit.?.sub_path);
1182 if (!header.isZerofill()) {
1183 segment.filesize = start;
38901184 }
1185 segment.vmsize = start;
38911186 }
38921187
3893 if (!options.disable_lld_caching) {
3894 // Update the file with the digest. If it fails we can continue; it only
3895 // means that the next invocation will have an unnecessary cache miss.
3896 Cache.writeSmallFile(directory.handle, id_symlink_basename, &digest) catch |err| {
3897 log.debug("failed to save linking hash digest file: {s}", .{@errorName(err)});
3898 };
3899 // Again failure here only means an unnecessary cache miss.
3900 if (man.have_exclusive_lock) {
3901 man.writeManifest() catch |err| {
3902 log.debug("failed to write cache manifest when linking: {s}", .{@errorName(err)});
3903 };
3904 }
3905 // We hang on to this lock so that the output file path can be used without
3906 // other processes clobbering it.
3907 macho_file.base.lock = man.toOwnedLock();
3908 }
1188 const page_size = MachO.getPageSize(macho_file.base.options.target.cpu.arch);
1189 segment.filesize = mem.alignForward(u64, segment.filesize, page_size);
1190 segment.vmsize = mem.alignForward(u64, segment.vmsize, page_size);
39091191}
39101192
3911/// Binary search
3912pub fn bsearch(comptime T: type, haystack: []align(1) const T, predicate: anytype) usize {
3913 if (!@hasDecl(@TypeOf(predicate), "predicate"))
3914 @compileError("Predicate is required to define fn predicate(@This(), T) bool");
3915
3916 var min: usize = 0;
3917 var max: usize = haystack.len;
3918 while (min < max) {
3919 const index = (min + max) / 2;
3920 const curr = haystack[index];
3921 if (predicate.predicate(curr)) {
3922 min = index + 1;
3923 } else {
3924 max = index;
3925 }
3926 }
3927 return min;
3928}
1193const std = @import("std");
1194const build_options = @import("build_options");
1195const assert = std.debug.assert;
1196const dwarf = std.dwarf;
1197const fs = std.fs;
1198const log = std.log.scoped(.link);
1199const macho = std.macho;
1200const math = std.math;
1201const mem = std.mem;
39291202
3930/// Linear search
3931pub fn lsearch(comptime T: type, haystack: []align(1) const T, predicate: anytype) usize {
3932 if (!@hasDecl(@TypeOf(predicate), "predicate"))
3933 @compileError("Predicate is required to define fn predicate(@This(), T) bool");
1203const aarch64 = @import("../../arch/aarch64/bits.zig");
1204const calcUuid = @import("uuid.zig").calcUuid;
1205const dead_strip = @import("dead_strip.zig");
1206const eh_frame = @import("eh_frame.zig");
1207const fat = @import("fat.zig");
1208const link = @import("../../link.zig");
1209const load_commands = @import("load_commands.zig");
1210const stubs = @import("stubs.zig");
1211const thunks = @import("thunks.zig");
1212const trace = @import("../../tracy.zig").trace;
39341213
3935 var i: usize = 0;
3936 while (i < haystack.len) : (i += 1) {
3937 if (predicate.predicate(haystack[i])) break;
3938 }
3939 return i;
3940}
1214const Allocator = mem.Allocator;
1215const Archive = @import("Archive.zig");
1216const Atom = @import("Atom.zig");
1217const Cache = std.Build.Cache;
1218const CodeSignature = @import("CodeSignature.zig");
1219const Compilation = @import("../../Compilation.zig");
1220const Dylib = @import("Dylib.zig");
1221const MachO = @import("../MachO.zig");
1222const Md5 = std.crypto.hash.Md5;
1223const LibStub = @import("../tapi.zig").LibStub;
1224const Object = @import("Object.zig");
1225const Platform = load_commands.Platform;
1226const Section = MachO.Section;
1227const StringTable = @import("../strtab.zig").StringTable;
1228const SymbolWithLoc = MachO.SymbolWithLoc;
1229const TableSection = @import("../table_section.zig").TableSection;
1230const Trie = @import("Trie.zig");
1231const UnwindInfo = @import("UnwindInfo.zig");
src/link/tapi.zig+32-2
......@@ -2,9 +2,10 @@ const std = @import("std");
22const fs = std.fs;
33const mem = std.mem;
44const log = std.log.scoped(.tapi);
5const yaml = @import("tapi/yaml.zig");
56
67const Allocator = mem.Allocator;
7const Yaml = @import("tapi/yaml.zig").Yaml;
8const Yaml = yaml.Yaml;
89
910const VersionField = union(enum) {
1011 string: []const u8,
......@@ -80,6 +81,30 @@ pub const Tbd = union(enum) {
8081 v3: TbdV3,
8182 v4: TbdV4,
8283
84 /// Caller owns memory.
85 pub fn targets(self: Tbd, gpa: Allocator) error{OutOfMemory}![]const []const u8 {
86 var out = std.ArrayList([]const u8).init(gpa);
87 defer out.deinit();
88
89 switch (self) {
90 .v3 => |v3| {
91 try out.ensureTotalCapacityPrecise(v3.archs.len);
92 for (v3.archs) |arch| {
93 const target = try std.fmt.allocPrint(gpa, "{s}-{s}", .{ arch, v3.platform });
94 out.appendAssumeCapacity(target);
95 }
96 },
97 .v4 => |v4| {
98 try out.ensureTotalCapacityPrecise(v4.targets.len);
99 for (v4.targets) |t| {
100 out.appendAssumeCapacity(try gpa.dupe(u8, t));
101 }
102 },
103 }
104
105 return out.toOwnedSlice();
106 }
107
83108 pub fn currentVersion(self: Tbd) ?VersionField {
84109 return switch (self) {
85110 .v3 => |v3| v3.current_version,
......@@ -102,6 +127,11 @@ pub const Tbd = union(enum) {
102127 }
103128};
104129
130pub const TapiError = error{
131 NotLibStub,
132 FileTooBig,
133} || yaml.YamlError || std.fs.File.ReadError;
134
105135pub const LibStub = struct {
106136 /// Underlying memory for stub's contents.
107137 yaml: Yaml,
......@@ -109,7 +139,7 @@ pub const LibStub = struct {
109139 /// Typed contents of the tbd file.
110140 inner: []Tbd,
111141
112 pub fn loadFromFile(allocator: Allocator, file: fs.File) !LibStub {
142 pub fn loadFromFile(allocator: Allocator, file: fs.File) TapiError!LibStub {
113143 const source = try file.readToEndAlloc(allocator, std.math.maxInt(u32));
114144 defer allocator.free(source);
115145