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...@@ -588,7 +588,6 @@ set(ZIG_STAGE2_SOURCES
588 "${CMAKE_SOURCE_DIR}/src/link/MachO/Relocation.zig"588 "${CMAKE_SOURCE_DIR}/src/link/MachO/Relocation.zig"
589 "${CMAKE_SOURCE_DIR}/src/link/MachO/Trie.zig"589 "${CMAKE_SOURCE_DIR}/src/link/MachO/Trie.zig"
590 "${CMAKE_SOURCE_DIR}/src/link/MachO/UnwindInfo.zig"590 "${CMAKE_SOURCE_DIR}/src/link/MachO/UnwindInfo.zig"
591 "${CMAKE_SOURCE_DIR}/src/link/MachO/ZldAtom.zig"
592 "${CMAKE_SOURCE_DIR}/src/link/MachO/dyld_info/bind.zig"591 "${CMAKE_SOURCE_DIR}/src/link/MachO/dyld_info/bind.zig"
593 "${CMAKE_SOURCE_DIR}/src/link/MachO/dyld_info/Rebase.zig"592 "${CMAKE_SOURCE_DIR}/src/link/MachO/dyld_info/Rebase.zig"
594 "${CMAKE_SOURCE_DIR}/src/link/MachO/dead_strip.zig"593 "${CMAKE_SOURCE_DIR}/src/link/MachO/dead_strip.zig"
src/Compilation.zig+16
...@@ -2609,6 +2609,9 @@ pub fn totalErrorCount(self: *Compilation) u32 {...@@ -2609,6 +2609,9 @@ pub fn totalErrorCount(self: *Compilation) u32 {
2609 }2609 }
2610 total += @intFromBool(self.link_error_flags.missing_libc);2610 total += @intFromBool(self.link_error_flags.missing_libc);
26112611
2612 // Misc linker errors
2613 total += self.bin_file.miscErrors().len;
2614
2612 // Compile log errors only count if there are no other errors.2615 // Compile log errors only count if there are no other errors.
2613 if (total == 0) {2616 if (total == 0) {
2614 if (self.bin_file.options.module) |module| {2617 if (self.bin_file.options.module) |module| {
...@@ -2759,6 +2762,19 @@ pub fn getAllErrorsAlloc(self: *Compilation) !ErrorBundle {...@@ -2759,6 +2762,19 @@ pub fn getAllErrorsAlloc(self: *Compilation) !ErrorBundle {
2759 }));2762 }));
2760 }2763 }
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
2762 if (self.bin_file.options.module) |module| {2778 if (self.bin_file.options.module) |module| {
2763 if (bundle.root_list.items.len == 0 and module.compile_log_decls.count() != 0) {2779 if (bundle.root_list.items.len == 0 and module.compile_log_decls.count() != 0) {
2764 const keys = module.compile_log_decls.keys();2780 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 {...@@ -670,7 +670,7 @@ fn mirCallExtern(emit: *Emit, inst: Mir.Inst.Index) !void {
670670
671 if (emit.bin_file.cast(link.File.MachO)) |macho_file| {671 if (emit.bin_file.cast(link.File.MachO)) |macho_file| {
672 // Add relocation to the decl.672 // 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 }).?;
674 const target = macho_file.getGlobalByIndex(relocation.sym_index);674 const target = macho_file.getGlobalByIndex(relocation.sym_index);
675 try link.File.MachO.Atom.addRelocation(macho_file, atom_index, .{675 try link.File.MachO.Atom.addRelocation(macho_file, atom_index, .{
676 .type = .branch,676 .type = .branch,
...@@ -885,9 +885,9 @@ fn mirLoadMemoryPie(emit: *Emit, inst: Mir.Inst.Index) !void {...@@ -885,9 +885,9 @@ fn mirLoadMemoryPie(emit: *Emit, inst: Mir.Inst.Index) !void {
885 if (emit.bin_file.cast(link.File.MachO)) |macho_file| {885 if (emit.bin_file.cast(link.File.MachO)) |macho_file| {
886 const Atom = link.File.MachO.Atom;886 const Atom = link.File.MachO.Atom;
887 const Relocation = Atom.Relocation;887 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 }).?;
889 try Atom.addRelocations(macho_file, atom_index, &[_]Relocation{ .{889 try Atom.addRelocations(macho_file, atom_index, &[_]Relocation{ .{
890 .target = .{ .sym_index = data.sym_index, .file = null },890 .target = .{ .sym_index = data.sym_index },
891 .offset = offset,891 .offset = offset,
892 .addend = 0,892 .addend = 0,
893 .pcrel = true,893 .pcrel = true,
...@@ -898,7 +898,7 @@ fn mirLoadMemoryPie(emit: *Emit, inst: Mir.Inst.Index) !void {...@@ -898,7 +898,7 @@ fn mirLoadMemoryPie(emit: *Emit, inst: Mir.Inst.Index) !void {
898 else => unreachable,898 else => unreachable,
899 },899 },
900 }, .{900 }, .{
901 .target = .{ .sym_index = data.sym_index, .file = null },901 .target = .{ .sym_index = data.sym_index },
902 .offset = offset + 4,902 .offset = offset + 4,
903 .addend = 0,903 .addend = 0,
904 .pcrel = false,904 .pcrel = false,
src/arch/x86_64/Emit.zig+3-8
...@@ -43,9 +43,7 @@ pub fn emitMir(emit: *Emit) Error!void {...@@ -43,9 +43,7 @@ pub fn emitMir(emit: *Emit) Error!void {
43 }),43 }),
44 .linker_extern_fn => |symbol| if (emit.bin_file.cast(link.File.MachO)) |macho_file| {44 .linker_extern_fn => |symbol| if (emit.bin_file.cast(link.File.MachO)) |macho_file| {
45 // Add relocation to the decl.45 // Add relocation to the decl.
46 const atom_index = macho_file.getAtomIndexForSymbol(46 const atom_index = macho_file.getAtomIndexForSymbol(.{ .sym_index = symbol.atom_index }).?;
47 .{ .sym_index = symbol.atom_index, .file = null },
48 ).?;
49 const target = macho_file.getGlobalByIndex(symbol.sym_index);47 const target = macho_file.getGlobalByIndex(symbol.sym_index);
50 try link.File.MachO.Atom.addRelocation(macho_file, atom_index, .{48 try link.File.MachO.Atom.addRelocation(macho_file, atom_index, .{
51 .type = .branch,49 .type = .branch,
...@@ -77,10 +75,7 @@ pub fn emitMir(emit: *Emit) Error!void {...@@ -77,10 +75,7 @@ pub fn emitMir(emit: *Emit) Error!void {
77 .linker_import,75 .linker_import,
78 .linker_tlv,76 .linker_tlv,
79 => |symbol| if (emit.bin_file.cast(link.File.MachO)) |macho_file| {77 => |symbol| if (emit.bin_file.cast(link.File.MachO)) |macho_file| {
80 const atom_index = macho_file.getAtomIndexForSymbol(.{78 const atom_index = macho_file.getAtomIndexForSymbol(.{ .sym_index = symbol.atom_index }).?;
81 .sym_index = symbol.atom_index,
82 .file = null,
83 }).?;
84 try link.File.MachO.Atom.addRelocation(macho_file, atom_index, .{79 try link.File.MachO.Atom.addRelocation(macho_file, atom_index, .{
85 .type = switch (lowered_relocs[0].target) {80 .type = switch (lowered_relocs[0].target) {
86 .linker_got => .got,81 .linker_got => .got,
...@@ -88,7 +83,7 @@ pub fn emitMir(emit: *Emit) Error!void {...@@ -88,7 +83,7 @@ pub fn emitMir(emit: *Emit) Error!void {
88 .linker_tlv => .tlv,83 .linker_tlv => .tlv,
89 else => unreachable,84 else => unreachable,
90 },85 },
91 .target = .{ .sym_index = symbol.sym_index, .file = null },86 .target = .{ .sym_index = symbol.sym_index },
92 .offset = @as(u32, @intCast(end_offset - 4)),87 .offset = @as(u32, @intCast(end_offset - 4)),
93 .addend = 0,88 .addend = 0,
94 .pcrel = true,89 .pcrel = true,
src/link.zig+20-19
...@@ -228,7 +228,6 @@ pub const Options = struct {...@@ -228,7 +228,6 @@ pub const Options = struct {
228228
229 version: ?std.SemanticVersion,229 version: ?std.SemanticVersion,
230 compatibility_version: ?std.SemanticVersion,230 compatibility_version: ?std.SemanticVersion,
231 darwin_sdk_version: ?std.SemanticVersion = null,
232 libc_installation: ?*const LibCInstallation,231 libc_installation: ?*const LibCInstallation,
233232
234 dwarf_format: ?std.dwarf.Format,233 dwarf_format: ?std.dwarf.Format,
...@@ -694,19 +693,15 @@ pub const File = struct {...@@ -694,19 +693,15 @@ pub const File = struct {
694 /// TODO audit this error set. most of these should be collapsed into one error,693 /// TODO audit this error set. most of these should be collapsed into one error,
695 /// and ErrorFlags should be updated to convey the meaning to the user.694 /// and ErrorFlags should be updated to convey the meaning to the user.
696 pub const FlushError = error{695 pub const FlushError = error{
697 BadDwarfCfi,
698 CacheUnavailable,696 CacheUnavailable,
699 CurrentWorkingDirectoryUnlinked,697 CurrentWorkingDirectoryUnlinked,
700 DivisionByZero,698 DivisionByZero,
701 DllImportLibraryNotFound,699 DllImportLibraryNotFound,
702 EmptyStubFile,
703 ExpectedFuncType,700 ExpectedFuncType,
704 FailedToEmit,701 FailedToEmit,
705 FailedToResolveRelocationTarget,
706 FileSystem,702 FileSystem,
707 FilesOpenedWithWrongFlags,703 FilesOpenedWithWrongFlags,
708 FlushFailure,704 FlushFailure,
709 FrameworkNotFound,
710 FunctionSignatureMismatch,705 FunctionSignatureMismatch,
711 GlobalTypeMismatch,706 GlobalTypeMismatch,
712 HotSwapUnavailableOnHostOperatingSystem,707 HotSwapUnavailableOnHostOperatingSystem,
...@@ -723,27 +718,19 @@ pub const File = struct {...@@ -723,27 +718,19 @@ pub const File = struct {
723 LLD_LinkingIsTODO_ForSpirV,718 LLD_LinkingIsTODO_ForSpirV,
724 LibCInstallationMissingCRTDir,719 LibCInstallationMissingCRTDir,
725 LibCInstallationNotAvailable,720 LibCInstallationNotAvailable,
726 LibraryNotFound,
727 LinkingWithoutZigSourceUnimplemented,721 LinkingWithoutZigSourceUnimplemented,
728 MalformedArchive,722 MalformedArchive,
729 MalformedDwarf,723 MalformedDwarf,
730 MalformedSection,724 MalformedSection,
731 MemoryTooBig,725 MemoryTooBig,
732 MemoryTooSmall,726 MemoryTooSmall,
733 MismatchedCpuArchitecture,
734 MissAlignment,727 MissAlignment,
735 MissingEndForBody,728 MissingEndForBody,
736 MissingEndForExpression,729 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,
741 MissingSymbol,730 MissingSymbol,
742 MissingTableSymbols,731 MissingTableSymbols,
743 ModuleNameMismatch,732 ModuleNameMismatch,
744 MultipleSymbolDefinitions,
745 NoObjectsToLink,733 NoObjectsToLink,
746 NotObject,
747 NotObjectFile,734 NotObjectFile,
748 NotSupported,735 NotSupported,
749 OutOfMemory,736 OutOfMemory,
...@@ -755,21 +742,15 @@ pub const File = struct {...@@ -755,21 +742,15 @@ pub const File = struct {
755 SymbolMismatchingType,742 SymbolMismatchingType,
756 TODOImplementPlan9Objs,743 TODOImplementPlan9Objs,
757 TODOImplementWritingLibFiles,744 TODOImplementWritingLibFiles,
758 TODOImplementWritingStaticLibFiles,
759 UnableToSpawnSelf,745 UnableToSpawnSelf,
760 UnableToSpawnWasm,746 UnableToSpawnWasm,
761 UnableToWriteArchive,747 UnableToWriteArchive,
762 UndefinedLocal,748 UndefinedLocal,
763 /// TODO: merge with UndefinedSymbolReference
764 UndefinedSymbol,749 UndefinedSymbol,
765 /// TODO: merge with UndefinedSymbol
766 UndefinedSymbolReference,
767 Underflow,750 Underflow,
768 UnexpectedRemainder,751 UnexpectedRemainder,
769 UnexpectedTable,752 UnexpectedTable,
770 UnexpectedValue,753 UnexpectedValue,
771 UnhandledDwFormValue,
772 UnhandledSymbolType,
773 UnknownFeature,754 UnknownFeature,
774 Unseekable,755 Unseekable,
775 UnsupportedCpuArchitecture,756 UnsupportedCpuArchitecture,
...@@ -866,6 +847,13 @@ pub const File = struct {...@@ -866,6 +847,13 @@ pub const File = struct {
866 }847 }
867 }848 }
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
869 pub const UpdateDeclExportsError = error{857 pub const UpdateDeclExportsError = error{
870 OutOfMemory,858 OutOfMemory,
871 AnalysisFail,859 AnalysisFail,
...@@ -1129,6 +1117,19 @@ pub const File = struct {...@@ -1129,6 +1117,19 @@ pub const File = struct {
1129 missing_libc: bool = false,1117 missing_libc: bool = false,
1130 };1118 };
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
1132 pub const LazySymbol = struct {1133 pub const LazySymbol = struct {
1133 pub const Kind = enum { code, const_data };1134 pub const Kind = enum { code, const_data };
11341135
src/link/MachO.zig+2250-908
...@@ -1,100 +1,3 @@...@@ -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
98base: File,1base: File,
992
100/// If this is not null, an object file is created by LLVM and linked with LLD afterwards.3/// 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,...@@ -103,18 +6,18 @@ llvm_object: ?*LlvmObject = null,
103/// Debug symbols bundle (or dSym).6/// Debug symbols bundle (or dSym).
104d_sym: ?DebugSymbols = null,7d_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
110mode: Mode,9mode: Mode,
11110
112dyld_info_cmd: macho.dyld_info_command = .{},11dyld_info_cmd: macho.dyld_info_command = .{},
113symtab_cmd: macho.symtab_command = .{},12symtab_cmd: macho.symtab_command = .{},
114dysymtab_cmd: macho.dysymtab_command = .{},13dysymtab_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 },
116codesig_cmd: macho.linkedit_data_command = .{ .cmd = .CODE_SIGNATURE },17codesig_cmd: macho.linkedit_data_command = .{ .cmd = .CODE_SIGNATURE },
11718
19objects: std.ArrayListUnmanaged(Object) = .{},
20archives: std.ArrayListUnmanaged(Archive) = .{},
118dylibs: std.ArrayListUnmanaged(Dylib) = .{},21dylibs: std.ArrayListUnmanaged(Dylib) = .{},
119dylibs_map: std.StringHashMapUnmanaged(u16) = .{},22dylibs_map: std.StringHashMapUnmanaged(u16) = .{},
120referenced_dylibs: std.AutoArrayHashMapUnmanaged(u16, void) = .{},23referenced_dylibs: std.AutoArrayHashMapUnmanaged(u16, void) = .{},
...@@ -130,14 +33,19 @@ data_segment_cmd_index: ?u8 = null,...@@ -130,14 +33,19 @@ data_segment_cmd_index: ?u8 = null,
130linkedit_segment_cmd_index: ?u8 = null,33linkedit_segment_cmd_index: ?u8 = null,
13134
132text_section_index: ?u8 = null,35text_section_index: ?u8 = null,
133stubs_section_index: ?u8 = null,
134stub_helper_section_index: ?u8 = null,
135got_section_index: ?u8 = null,
136data_const_section_index: ?u8 = null,36data_const_section_index: ?u8 = null,
137la_symbol_ptr_section_index: ?u8 = null,
138data_section_index: ?u8 = null,37data_section_index: ?u8 = null,
38bss_section_index: ?u8 = null,
139thread_vars_section_index: ?u8 = null,39thread_vars_section_index: ?u8 = null,
140thread_data_section_index: ?u8 = null,40thread_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
142locals: std.ArrayListUnmanaged(macho.nlist_64) = .{},50locals: std.ArrayListUnmanaged(macho.nlist_64) = .{},
143globals: std.ArrayListUnmanaged(SymbolWithLoc) = .{},51globals: std.ArrayListUnmanaged(SymbolWithLoc) = .{},
...@@ -154,8 +62,13 @@ strtab: StringTable(.strtab) = .{},...@@ -154,8 +62,13 @@ strtab: StringTable(.strtab) = .{},
15462
155got_table: TableSection(SymbolWithLoc) = .{},63got_table: TableSection(SymbolWithLoc) = .{},
156stub_table: TableSection(SymbolWithLoc) = .{},64stub_table: TableSection(SymbolWithLoc) = .{},
65tlv_ptr_table: TableSection(SymbolWithLoc) = .{},
66
67thunk_table: std.AutoHashMapUnmanaged(Atom.Index, thunks.Thunk.Index) = .{},
68thunks: std.ArrayListUnmanaged(thunks.Thunk) = .{},
15769
158error_flags: File.ErrorFlags = File.ErrorFlags{},70error_flags: File.ErrorFlags = File.ErrorFlags{},
71misc_errors: std.ArrayListUnmanaged(File.ErrorMsg) = .{},
15972
160segment_table_dirty: bool = false,73segment_table_dirty: bool = false,
161got_table_count_dirty: bool = false,74got_table_count_dirty: bool = false,
...@@ -225,108 +138,6 @@ tlv_table: TlvSymbolTable = .{},...@@ -225,108 +138,6 @@ tlv_table: TlvSymbolTable = .{},
225/// Hot-code swapping state.138/// Hot-code swapping state.
226hot_state: if (is_hot_update_compatible) HotUpdateState else struct {} = .{},139hot_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
330pub fn openPath(allocator: Allocator, options: link.Options) !*MachO {141pub fn openPath(allocator: Allocator, options: link.Options) !*MachO {
331 assert(options.target.ofmt == .macho);142 assert(options.target.ofmt == .macho);
332143
...@@ -396,7 +207,6 @@ pub fn openPath(allocator: Allocator, options: link.Options) !*MachO {...@@ -396,7 +207,6 @@ pub fn openPath(allocator: Allocator, options: link.Options) !*MachO {
396 .allocator = allocator,207 .allocator = allocator,
397 .dwarf = link.File.Dwarf.init(allocator, &self.base, options.target),208 .dwarf = link.File.Dwarf.init(allocator, &self.base, options.target),
398 .file = d_sym_file,209 .file = d_sym_file,
399 .page_size = self.page_size,
400 };210 };
401 }211 }
402212
...@@ -413,16 +223,13 @@ pub fn openPath(allocator: Allocator, options: link.Options) !*MachO {...@@ -413,16 +223,13 @@ pub fn openPath(allocator: Allocator, options: link.Options) !*MachO {
413 try self.populateMissingMetadata();223 try self.populateMissingMetadata();
414224
415 if (self.d_sym) |*d_sym| {225 if (self.d_sym) |*d_sym| {
416 try d_sym.populateMissingMetadata();226 try d_sym.populateMissingMetadata(self);
417 }227 }
418228
419 return self;229 return self;
420}230}
421231
422pub fn createEmpty(gpa: Allocator, options: link.Options) !*MachO {232pub 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
426 const self = try gpa.create(MachO);233 const self = try gpa.create(MachO);
427 errdefer gpa.destroy(self);234 errdefer gpa.destroy(self);
428235
...@@ -433,7 +240,6 @@ pub fn createEmpty(gpa: Allocator, options: link.Options) !*MachO {...@@ -433,7 +240,6 @@ pub fn createEmpty(gpa: Allocator, options: link.Options) !*MachO {
433 .allocator = gpa,240 .allocator = gpa,
434 .file = null,241 .file = null,
435 },242 },
436 .page_size = page_size,
437 .mode = if (options.use_llvm or options.module == null or options.cache_mode == .whole)243 .mode = if (options.use_llvm or options.module == null or options.cache_mode == .whole)
438 .zld244 .zld
439 else245 else
...@@ -461,8 +267,11 @@ pub fn flush(self: *MachO, comp: *Compilation, prog_node: *std.Progress.Node) li...@@ -461,8 +267,11 @@ pub fn flush(self: *MachO, comp: *Compilation, prog_node: *std.Progress.Node) li
461 if (build_options.have_llvm) {267 if (build_options.have_llvm) {
462 return self.base.linkAsArchive(comp, prog_node);268 return self.base.linkAsArchive(comp, prog_node);
463 } else {269 } else {
464 log.err("TODO: non-LLVM archiver for MachO object files", .{});270 try self.misc_errors.ensureUnusedCapacity(self.base.allocator, 1);
465 return error.TODOImplementWritingStaticLibFiles;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;
466 }275 }
467 }276 }
468277
...@@ -590,36 +399,40 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No...@@ -590,36 +399,40 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No
590 self.dylibs_map.clearRetainingCapacity();399 self.dylibs_map.clearRetainingCapacity();
591 self.referenced_dylibs.clearRetainingCapacity();400 self.referenced_dylibs.clearRetainingCapacity();
592401
593 var dependent_libs = std.fifo.LinearFifo(struct {402 var dependent_libs = std.fifo.LinearFifo(DylibReExportInfo, .Dynamic).init(arena);
594 id: Dylib.Id,
595 parent: u16,
596 }, .Dynamic).init(arena);
597403
598 try self.parseLibs(libs.keys(), libs.values(), self.base.options.sysroot, &dependent_libs);404 for (libs.keys(), libs.values()) |path, lib| {
599 try self.parseDependentLibs(self.base.options.sysroot, &dependent_libs);405 const in_file = try std.fs.cwd().openFile(path, .{});
600 }406 defer in_file.close();
601407
602 if (self.dyld_stub_binder_index == null) {408 var parse_ctx = ParseErrorCtx.init(self.base.allocator);
603 self.dyld_stub_binder_index = try self.addUndefined("dyld_stub_binder", .add_got);409 defer parse_ctx.deinit();
604 }
605 if (!self.base.options.single_threaded) {
606 _ = try self.addUndefined("__tlv_bootstrap", .none);
607 }
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
611 var actions = std.ArrayList(ResolveAction).init(self.base.allocator);426 var actions = std.ArrayList(ResolveAction).init(self.base.allocator);
612 defer actions.deinit();427 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 }
615 if (self.unresolved.count() > 0) {433 if (self.unresolved.count() > 0) {
616 for (self.unresolved.keys()) |index| {434 try self.reportUndefined();
617 // TODO: convert into compiler errors.435 return error.FlushFailure;
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;
623 }436 }
624437
625 for (actions.items) |action| switch (action.kind) {438 for (actions.items) |action| switch (action.kind) {
...@@ -631,6 +444,16 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No...@@ -631,6 +444,16 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No
631 try self.createDyldPrivateAtom();444 try self.createDyldPrivateAtom();
632 try self.writeStubHelperPreamble();445 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
634 try self.allocateSpecialSymbols();457 try self.allocateSpecialSymbols();
635458
636 for (self.relocs.keys()) |atom_index| {459 for (self.relocs.keys()) |atom_index| {
...@@ -685,20 +508,13 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No...@@ -685,20 +508,13 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No
685508
686 try self.writeLinkeditSegmentData();509 try self.writeLinkeditSegmentData();
687510
688 const target = self.base.options.target;511 var codesig: ?CodeSignature = if (requiresCodeSignature(&self.base.options)) blk: {
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: {
696 // Preallocate space for the code signature.512 // Preallocate space for the code signature.
697 // We need to do this at this stage so that we have the load commands with proper values513 // We need to do this at this stage so that we have the load commands with proper values
698 // written out to the file.514 // written out to the file.
699 // The most important here is to have the correct vm and filesize of the __LINKEDIT segment515 // The most important here is to have the correct vm and filesize of the __LINKEDIT segment
700 // where the code signature goes into.516 // 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));
702 codesig.code_directory.ident = self.base.options.emit.?.sub_path;518 codesig.code_directory.ident = self.base.options.emit.?.sub_path;
703 if (self.base.options.entitlements) |path| {519 if (self.base.options.entitlements) |path| {
704 try codesig.addEntitlements(self.base.allocator, path);520 try codesig.addEntitlements(self.base.allocator, path);
...@@ -722,16 +538,18 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No...@@ -722,16 +538,18 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No
722 .Exe => blk: {538 .Exe => blk: {
723 const seg_id = self.header_segment_cmd_index.?;539 const seg_id = self.header_segment_cmd_index.?;
724 const seg = self.segments.items[seg_id];540 const seg = self.segments.items[seg_id];
725 const global = self.getEntryPoint() catch |err| switch (err) {541 const global = self.getEntryPoint() orelse break :blk;
726 error.MissingMainEntrypoint => {
727 self.error_flags.no_entry_point_found = true;
728 break :blk;
729 },
730 else => |e| return e,
731 };
732 const sym = self.getSymbol(global);542 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
733 try lc_writer.writeStruct(macho.entry_point_command{551 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)),
735 .stacksize = self.base.options.stack_size_override orelse 0,553 .stacksize = self.base.options.stack_size_override orelse 0,
736 });554 });
737 },555 },
...@@ -745,21 +563,32 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No...@@ -745,21 +563,32 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No
745 try lc_writer.writeStruct(macho.source_version_command{563 try lc_writer.writeStruct(macho.source_version_command{
746 .version = 0,564 .version = 0,
747 });565 });
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
750 const uuid_cmd_offset = @sizeOf(macho.mach_header_64) + @as(u32, @intCast(lc_buffer.items.len));579 const uuid_cmd_offset = @sizeOf(macho.mach_header_64) + @as(u32, @intCast(lc_buffer.items.len));
751 try lc_writer.writeStruct(self.uuid_cmd);580 try lc_writer.writeStruct(self.uuid_cmd);
752581
753 try load_commands.writeLoadDylibLCs(self.dylibs.items, self.referenced_dylibs.keys(), lc_writer);582 try load_commands.writeLoadDylibLCs(self.dylibs.items, self.referenced_dylibs.keys(), lc_writer);
754583
755 if (requires_codesig) {584 if (codesig != null) {
756 try lc_writer.writeStruct(self.codesig_cmd);585 try lc_writer.writeStruct(self.codesig_cmd);
757 }586 }
758587
759 const ncmds = load_commands.calcNumOfLCs(lc_buffer.items);588 const ncmds = load_commands.calcNumOfLCs(lc_buffer.items);
760 try self.base.file.?.pwriteAll(lc_buffer.items, @sizeOf(macho.mach_header_64));589 try self.base.file.?.pwriteAll(lc_buffer.items, @sizeOf(macho.mach_header_64));
761 try self.writeHeader(ncmds, @as(u32, @intCast(lc_buffer.items.len)));590 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
764 if (codesig) |*csig| {593 if (codesig) |*csig| {
765 try self.writeCodeSignature(comp, csig); // code signing always comes last594 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...@@ -772,11 +601,6 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No
772 try d_sym.flushModule(self);601 try d_sym.flushModule(self);
773 }602 }
774603
775 // if (build_options.enable_link_snapshots) {
776 // if (self.base.options.enable_link_snapshots)
777 // try self.snapshotState();
778 // }
779
780 if (cache_miss) {604 if (cache_miss) {
781 // Update the file with the digest. If it fails we can continue; it only605 // Update the file with the digest. If it fails we can continue; it only
782 // means that the next invocation will have an unnecessary cache miss.606 // 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:...@@ -850,7 +674,7 @@ fn resolveLibSystemInDirs(arena: Allocator, dirs: []const []const u8, out_libs:
850 // Try stub file first. If we hit it, then we're done as the stub file674 // Try stub file first. If we hit it, then we're done as the stub file
851 // re-exports every single symbol definition.675 // re-exports every single symbol definition.
852 for (dirs) |dir| {676 for (dirs) |dir| {
853 if (try resolveLib(arena, dir, "System", ".tbd")) |full_path| {677 if (try resolveLib(arena, dir, "libSystem", ".tbd")) |full_path| {
854 try out_libs.put(full_path, .{ .needed = true, .weak = false, .path = full_path });678 try out_libs.put(full_path, .{ .needed = true, .weak = false, .path = full_path });
855 return true;679 return true;
856 }680 }
...@@ -858,8 +682,8 @@ fn resolveLibSystemInDirs(arena: Allocator, dirs: []const []const u8, out_libs:...@@ -858,8 +682,8 @@ fn resolveLibSystemInDirs(arena: Allocator, dirs: []const []const u8, out_libs:
858 // If we didn't hit the stub file, try .dylib next. However, libSystem.dylib682 // If we didn't hit the stub file, try .dylib next. However, libSystem.dylib
859 // doesn't export libc.dylib which we'll need to resolve subsequently also.683 // doesn't export libc.dylib which we'll need to resolve subsequently also.
860 for (dirs) |dir| {684 for (dirs) |dir| {
861 if (try resolveLib(arena, dir, "System", ".dylib")) |libsystem_path| {685 if (try resolveLib(arena, dir, "libSystem", ".dylib")) |libsystem_path| {
862 if (try resolveLib(arena, dir, "c", ".dylib")) |libc_path| {686 if (try resolveLib(arena, dir, "libc", ".dylib")) |libc_path| {
863 try out_libs.put(libsystem_path, .{ .needed = true, .weak = false, .path = libsystem_path });687 try out_libs.put(libsystem_path, .{ .needed = true, .weak = false, .path = libsystem_path });
864 try out_libs.put(libc_path, .{ .needed = true, .weak = false, .path = libc_path });688 try out_libs.put(libc_path, .{ .needed = true, .weak = false, .path = libc_path });
865 return true;689 return true;
...@@ -876,7 +700,7 @@ fn resolveLib(...@@ -876,7 +700,7 @@ fn resolveLib(
876 name: []const u8,700 name: []const u8,
877 ext: []const u8,701 ext: []const u8,
878) !?[]const u8 {702) !?[]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 });
880 const full_path = try fs.path.join(arena, &[_][]const u8{ search_dir, search_name });704 const full_path = try fs.path.join(arena, &[_][]const u8{ search_dir, search_name });
881705
882 // Check if the file exists.706 // Check if the file exists.
...@@ -889,175 +713,388 @@ fn resolveLib(...@@ -889,175 +713,388 @@ fn resolveLib(
889 return full_path;713 return full_path;
890}714}
891715
892const ParseDylibError = error{716const ParseError = error{
717 UnknownFileType,
718 InvalidTarget,
719 InvalidTargetFatLibrary,
720 DylibAlreadyExists,
721 IncompatibleDylibVersion,
893 OutOfMemory,722 OutOfMemory,
894 EmptyStubFile,723 Overflow,
895 MismatchedCpuArchitecture,724 InputOutput,
896 UnsupportedCpuArchitecture,725 MalformedArchive,
726 NotLibStub,
897 EndOfStream,727 EndOfStream,
898} || fs.File.OpenError || std.os.PReadError || Dylib.Id.ParseError;728 FileSystem,
899729 NotSupported,
900const DylibCreateOpts = struct {730} || std.os.SeekError || std.fs.File.OpenError || std.fs.File.ReadError || tapi.TapiError;
901 syslibroot: ?[]const u8,
902 id: ?Dylib.Id = null,
903 dependent: bool = false,
904 needed: bool = false,
905 weak: bool = false,
906};
907731
908pub fn parseDylib(732pub fn parsePositional(
909 self: *MachO,733 self: *MachO,
734 file: std.fs.File,
910 path: []const u8,735 path: []const u8,
736 must_link: bool,
911 dependent_libs: anytype,737 dependent_libs: anytype,
912 opts: DylibCreateOpts,738 ctx: *ParseErrorCtx,
913) ParseDylibError!bool {739) ParseError!void {
914 const gpa = self.base.allocator;740 const tracy = trace(@src());
915 const file = fs.cwd().openFile(path, .{}) catch |err| switch (err) {741 defer tracy.end();
916 error.FileNotFound => return false,
917 else => |e| return e,
918 };
919 defer file.close();
920742
921 const cpu_arch = self.base.options.target.cpu.arch;743 if (Object.isObject(file)) {
922 const file_stat = try file.stat();744 try self.parseObject(file, path, ctx);
923 var file_size = math.cast(usize, file_stat.size) orelse return error.Overflow;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();754fn parseObject(
926 const fat_offset = math.cast(usize, try fat.getLibraryOffset(reader, cpu_arch)) orelse755 self: *MachO,
927 return error.Overflow;756 file: std.fs.File,
928 try file.seekTo(fat_offset);757 path: []const u8,
929 file_size -= fat_offset;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;
931 const contents = try file.readToEndAllocOptions(gpa, file_size, file_size, @alignOf(u64), null);770 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));772 var object = Object{
935 var dylib = Dylib{ .weak = opts.weak };773 .name = try gpa.dupe(u8, path),
936774 .mtime = mtime,
937 dylib.parseFromBinary(775 .contents = contents,
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,
964 };776 };
777 errdefer object.deinit(gpa);
778 try object.parse(gpa);
965779
966 if (opts.id) |id| {780 const detected_cpu_arch: std.Target.Cpu.Arch = switch (object.header.cputype) {
967 if (dylib.id.?.current_version < id.compatibility_version) {781 macho.CPU_TYPE_ARM64 => .aarch64,
968 log.warn("found dylib is incompatible with the required minimum version", .{});782 macho.CPU_TYPE_X86_64 => .x86_64,
969 log.warn(" dylib: {s}", .{id.name});783 else => unreachable,
970 log.warn(" required minimum version: {}", .{id.compatibility_version});784 };
971 log.warn(" dylib version: {}", .{dylib.id.?.current_version});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?789 if (this_cpu_arch != detected_cpu_arch or
974 dylib.deinit(gpa);790 (detected_platform != null and !detected_platform.?.eqlTarget(this_platform)))
975 return false;791 {
976 }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;
977 }795 }
978796
979 try self.dylibs.append(gpa, dylib);797 try self.objects.append(gpa, object);
980 try self.dylibs_map.putNoClobber(gpa, dylib.id.?.name, dylib_id);798}
981799
982 const should_link_dylib_even_if_unreachable = blk: {800pub fn parseLibrary(
983 if (self.base.options.dead_strip_dylibs and !opts.needed) break :blk false;801 self: *MachO,
984 break :blk !(opts.dependent or self.referenced_dylibs.contains(dylib_id));802 file: std.fs.File,
985 };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) {814 if (fat.isFatLibrary(file)) {
988 try self.referenced_dylibs.putNoClobber(gpa, dylib_id, {});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 };
989 }847 }
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;
992}871}
993872
994pub fn parseInputFiles(self: *MachO, files: []const []const u8, syslibroot: ?[]const u8, dependent_libs: anytype) !void {873fn parseArchive(
995 for (files) |file_name| {874 self: *MachO,
996 const full_path = full_path: {875 path: []const u8,
997 var buffer: [fs.MAX_PATH_BYTES]u8 = undefined;876 fat_offset: u64,
998 break :full_path try fs.realpath(file_name, &buffer);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,
999 };908 };
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;913 if (this_cpu_arch != detected_cpu_arch or
1003 if (try self.parseArchive(full_path, false)) continue;914 (detected_platform != null and !detected_platform.?.eqlTarget(this_platform)))
1004 if (try self.parseDylib(full_path, dependent_libs, .{915 {
1005 .syslibroot = syslibroot,916 const platform = detected_platform orelse this_platform;
1006 })) continue;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);
1009 }937 }
1010}938}
1011939
1012pub fn parseAndForceLoadStaticArchives(self: *MachO, files: []const []const u8) !void {940pub const DylibReExportInfo = struct {
1013 for (files) |file_name| {941 id: Dylib.Id,
1014 const full_path = full_path: {942 parent: u16,
1015 var buffer: [fs.MAX_PATH_BYTES]u8 = undefined;943};
1016 break :full_path try fs.realpath(file_name, &buffer);944
1017 };945const DylibOpts = struct {
1018 log.debug("parsing and force loading static archive '{s}'", .{full_path});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;988 if (this_cpu_arch != detected_cpu_arch or
1021 log.debug("unknown filetype: expected static archive: '{s}'", .{file_name});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;
1022 }994 }
995
996 try self.addDylib(dylib, dylib_options, ctx);
1023}997}
1024998
1025pub fn parseLibs(999fn parseLibStub(
1026 self: *MachO,1000 self: *MachO,
1027 lib_names: []const []const u8,1001 file: std.fs.File,
1028 lib_infos: []const link.SystemLib,1002 path: []const u8,
1029 syslibroot: ?[]const u8,
1030 dependent_libs: anytype,1003 dependent_libs: anytype,
1031) !void {1004 dylib_options: DylibOpts,
1032 for (lib_names, 0..) |lib, i| {1005 ctx: *ParseErrorCtx,
1033 const lib_info = lib_infos[i];1006) ParseError!void {
1034 log.debug("parsing lib path '{s}'", .{lib});1007 const gpa = self.base.allocator;
1035 if (try self.parseDylib(lib, dependent_libs, .{1008 var lib_stub = try LibStub.loadFromFile(gpa, file);
1036 .syslibroot = syslibroot,1009 defer lib_stub.deinit();
1037 .needed = lib_info.needed,1010
1038 .weak = lib_info.weak,1011 if (lib_stub.inner.len == 0) return error.NotLibStub;
1039 })) continue;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.*, {});
1042 }1074 }
1043}1075}
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
1046 // At this point, we can now parse dependents of dylibs preserving the inclusion order of:1081 // At this point, we can now parse dependents of dylibs preserving the inclusion order of:
1047 // 1) anything on the linker line is parsed first1082 // 1) anything on the linker line is parsed first
1048 // 2) afterwards, we parse dependents of the included dylibs1083 // 2) afterwards, we parse dependents of the included dylibs
1049 // TODO this should not be performed if the user specifies `-flat_namespace` flag.1084 // TODO this should not be performed if the user specifies `-flat_namespace` flag.
1050 // See ld64 manpages.1085 // 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);
1052 const arena = arena_alloc.allocator();1088 const arena = arena_alloc.allocator();
1053 defer arena_alloc.deinit();1089 defer arena_alloc.deinit();
10541090
1055 while (dependent_libs.readItem()) |*dep_id| {1091 while (dependent_libs.readItem()) |dep_id| {
1056 defer dep_id.id.deinit(self.base.allocator);1092 defer dep_id.id.deinit(gpa);
10571093
1058 if (self.dylibs_map.contains(dep_id.id.name)) continue;1094 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;
1061 const has_ext = blk: {1098 const has_ext = blk: {
1062 const basename = fs.path.basename(dep_id.id.name);1099 const basename = fs.path.basename(dep_id.id.name);
1063 break :blk mem.lastIndexOfScalar(u8, basename, '.') != null;1100 break :blk mem.lastIndexOfScalar(u8, basename, '.') != null;
...@@ -1068,22 +1105,45 @@ pub fn parseDependentLibs(self: *MachO, syslibroot: ?[]const u8, dependent_libs:...@@ -1068,22 +1105,45 @@ pub fn parseDependentLibs(self: *MachO, syslibroot: ?[]const u8, dependent_libs:
1068 break :blk dep_id.id.name[0..index];1105 break :blk dep_id.id.name[0..index];
1069 } else dep_id.id.name;1106 } else dep_id.id.name;
10701107
1071 for (&[_][]const u8{ extension, ".tbd" }) |ext| {1108 const maybe_full_path = full_path: {
1072 const with_ext = try std.fmt.allocPrint(arena, "{s}{s}", .{ without_ext, ext });1109 if (self.base.options.sysroot) |root| {
1073 const full_path = if (syslibroot) |root| try fs.path.join(arena, &.{ root, with_ext }) else with_ext;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, .{1122 const full_path = maybe_full_path orelse {
1078 .id = dep_id.id,1123 const parent_name = if (parent.id) |id| id.name else parent.path;
1079 .syslibroot = syslibroot,1124 try self.reportDependencyError(parent_name, null, "missing dynamic library dependency: '{s}'", .{
1080 .dependent = true,1125 dep_id.id.name,
1081 .weak = weak,
1082 });1126 });
1083 if (did_parse_successfully) break;1127 continue;
1084 } else {1128 };
1085 log.debug("unable to resolve dependency {s}", .{dep_id.id.name});1129
1086 }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
1087 }1147 }
1088}1148}
10891149
...@@ -1172,7 +1232,7 @@ fn writeStubHelperPreamble(self: *MachO) !void {...@@ -1172,7 +1232,7 @@ fn writeStubHelperPreamble(self: *MachO) !void {
11721232
1173 const gpa = self.base.allocator;1233 const gpa = self.base.allocator;
1174 const cpu_arch = self.base.options.target.cpu.arch;1234 const cpu_arch = self.base.options.target.cpu.arch;
1175 const size = stubs.calcStubHelperPreambleSize(cpu_arch);1235 const size = stubs.stubHelperPreambleSize(cpu_arch);
11761236
1177 var buf = try std.ArrayList(u8).initCapacity(gpa, size);1237 var buf = try std.ArrayList(u8).initCapacity(gpa, size);
1178 defer buf.deinit();1238 defer buf.deinit();
...@@ -1202,9 +1262,9 @@ fn writeStubTableEntry(self: *MachO, index: usize) !void {...@@ -1202,9 +1262,9 @@ fn writeStubTableEntry(self: *MachO, index: usize) !void {
1202 const laptr_sect_id = self.la_symbol_ptr_section_index.?;1262 const laptr_sect_id = self.la_symbol_ptr_section_index.?;
12031263
1204 const cpu_arch = self.base.options.target.cpu.arch;1264 const cpu_arch = self.base.options.target.cpu.arch;
1205 const stub_entry_size = stubs.calcStubEntrySize(cpu_arch);1265 const stub_entry_size = stubs.stubSize(cpu_arch);
1206 const stub_helper_entry_size = stubs.calcStubHelperEntrySize(cpu_arch);1266 const stub_helper_entry_size = stubs.stubHelperSize(cpu_arch);
1207 const stub_helper_preamble_size = stubs.calcStubHelperPreambleSize(cpu_arch);1267 const stub_helper_preamble_size = stubs.stubHelperPreambleSize(cpu_arch);
12081268
1209 if (self.stub_table_count_dirty) {1269 if (self.stub_table_count_dirty) {
1210 // We grow all 3 sections one by one.1270 // We grow all 3 sections one by one.
...@@ -1336,59 +1396,123 @@ pub fn allocateSpecialSymbols(self: *MachO) !void {...@@ -1336,59 +1396,123 @@ pub fn allocateSpecialSymbols(self: *MachO) !void {
1336 "__mh_execute_header",1396 "__mh_execute_header",
1337 }) |name| {1397 }) |name| {
1338 const global = self.getGlobal(name) orelse continue;1398 const global = self.getGlobal(name) orelse continue;
1339 if (global.file != null) continue;1399 if (global.getFile() != null) continue;
1340 const sym = self.getSymbolPtr(global);1400 const sym = self.getSymbolPtr(global);
1341 const seg = self.getSegment(self.text_section_index.?);1401 const seg = self.getSegment(self.text_section_index.?);
1342 sym.n_sect = 1;1402 sym.n_sect = self.text_section_index.? + 1;
1343 sym.n_value = seg.vmaddr;1403 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}", .{
1346 name,1406 name,
1407 sym.n_value,
1408 sym.n_sect,
1347 seg.segName(),1409 seg.segName(),
1348 });1410 });
1349 }1411 }
1350}1412}
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 {
1353 const gpa = self.base.allocator;1420 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));
1355 const atom = try self.atoms.addOne(gpa);1422 const atom = try self.atoms.addOne(gpa);
1356 const sym_index = try self.allocateSymbol();1423 atom.* = .{};
1357 try self.atom_by_index_table.putNoClobber(gpa, sym_index, atom_index);1424 atom.sym_index = sym_index;
1358 atom.* = .{1425 atom.size = opts.size;
1359 .sym_index = sym_index,1426 atom.alignment = opts.alignment;
1360 .file = null,1427 log.debug("creating ATOM(%{d}) at index {d}", .{ sym_index, index });
1361 .size = 0,1428 return index;
1362 .prev_index = null,1429}
1363 .next_index = null,1430
1364 };1431pub fn createTentativeDefAtoms(self: *MachO) !void {
1365 log.debug("creating ATOM(%{d}) at index {d}", .{ sym_index, atom_index });1432 const gpa = self.base.allocator;
1366 return atom_index;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 }
1367}1476}
13681477
1369fn createDyldPrivateAtom(self: *MachO) !void {1478pub fn createDyldPrivateAtom(self: *MachO) !void {
1370 if (self.dyld_private_atom_index != null) return;1479 if (self.dyld_private_atom_index != null) return;
13711480
1372 const atom_index = try self.createAtom();1481 const sym_index = try self.allocateSymbol();
1373 const atom = self.getAtomPtr(atom_index);1482 const atom_index = try self.createAtom(sym_index, .{
1374 atom.size = @sizeOf(u64);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);
1376 const sym = atom.getSymbolPtr(self);1493 const sym = atom.getSymbolPtr(self);
1377 sym.n_type = macho.N_SECT;1494 sym.n_type = macho.N_SECT;
1378 sym.n_sect = self.data_section_index.? + 1;1495 sym.n_sect = self.data_section_index.? + 1;
1379 self.dyld_private_atom_index = atom_index;1496 self.dyld_private_atom_index = atom_index;
13801497
1381 sym.n_value = try self.allocateAtom(atom_index, atom.size, @alignOf(u64));1498 switch (self.mode) {
1382 log.debug("allocated dyld_private atom at 0x{x}", .{sym.n_value});1499 .zld => self.addAtomToSection(atom_index),
1383 var buffer: [@sizeOf(u64)]u8 = [_]u8{0} ** @sizeOf(u64);1500 .incremental => {
1384 try self.writeAtom(atom_index, &buffer);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 }
1385}1507}
13861508
1387fn createThreadLocalDescriptorAtom(self: *MachO, sym_name: []const u8, target: SymbolWithLoc) !Atom.Index {1509fn createThreadLocalDescriptorAtom(self: *MachO, sym_name: []const u8, target: SymbolWithLoc) !Atom.Index {
1388 const gpa = self.base.allocator;1510 const gpa = self.base.allocator;
1389 const size = 3 * @sizeOf(u64);1511 const size = 3 * @sizeOf(u64);
1390 const required_alignment: u32 = 1;1512 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);
1392 self.getAtomPtr(atom_index).size = size;1516 self.getAtomPtr(atom_index).size = size;
13931517
1394 const sym = self.getAtom(atom_index).getSymbolPtr(self);1518 const sym = self.getAtom(atom_index).getSymbolPtr(self);
...@@ -1415,16 +1539,12 @@ fn createThreadLocalDescriptorAtom(self: *MachO, sym_name: []const u8, target: S...@@ -1415,16 +1539,12 @@ fn createThreadLocalDescriptorAtom(self: *MachO, sym_name: []const u8, target: S
1415 return atom_index;1539 return atom_index;
1416}1540}
14171541
1418fn createMhExecuteHeaderSymbol(self: *MachO) !void {1542pub fn createMhExecuteHeaderSymbol(self: *MachO) !void {
1419 if (self.base.options.output_mode != .Exe) return;1543 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
1425 const gpa = self.base.allocator;1545 const gpa = self.base.allocator;
1426 const sym_index = try self.allocateSymbol();1546 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 };
1428 const sym = self.getSymbolPtr(sym_loc);1548 const sym = self.getSymbolPtr(sym_loc);
1429 sym.* = .{1549 sym.* = .{
1430 .n_strx = try self.strtab.insert(gpa, "__mh_execute_header"),1550 .n_strx = try self.strtab.insert(gpa, "__mh_execute_header"),
...@@ -1435,16 +1555,23 @@ fn createMhExecuteHeaderSymbol(self: *MachO) !void {...@@ -1435,16 +1555,23 @@ fn createMhExecuteHeaderSymbol(self: *MachO) !void {
1435 };1555 };
14361556
1437 const gop = try self.getOrPutGlobalPtr("__mh_execute_header");1557 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 }
1438 gop.value_ptr.* = sym_loc;1565 gop.value_ptr.* = sym_loc;
1439}1566}
14401567
1441fn createDsoHandleSymbol(self: *MachO) !void {1568pub fn createDsoHandleSymbol(self: *MachO) !void {
1442 const global = self.getGlobalPtr("___dso_handle") orelse return;1569 const global = self.getGlobalPtr("___dso_handle") orelse return;
1443 if (!self.getSymbol(global.*).undf()) return;1570 if (!self.getSymbol(global.*).undf()) return;
14441571
1445 const gpa = self.base.allocator;1572 const gpa = self.base.allocator;
1446 const sym_index = try self.allocateSymbol();1573 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 };
1448 const sym = self.getSymbolPtr(sym_loc);1575 const sym = self.getSymbolPtr(sym_loc);
1449 sym.* = .{1576 sym.* = .{
1450 .n_strx = try self.strtab.insert(gpa, "___dso_handle"),1577 .n_strx = try self.strtab.insert(gpa, "___dso_handle"),
...@@ -1453,10 +1580,51 @@ fn createDsoHandleSymbol(self: *MachO) !void {...@@ -1453,10 +1580,51 @@ fn createDsoHandleSymbol(self: *MachO) !void {
1453 .n_desc = macho.N_WEAK_DEF,1580 .n_desc = macho.N_WEAK_DEF,
1454 .n_value = 0,1581 .n_value = 0,
1455 };1582 };
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 }
1456 global.* = sym_loc;1588 global.* = sym_loc;
1457 _ = self.unresolved.swapRemove(self.getGlobalIndex("___dso_handle").?);1589 _ = self.unresolved.swapRemove(self.getGlobalIndex("___dso_handle").?);
1458}1590}
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
1460fn resolveGlobalSymbol(self: *MachO, current: SymbolWithLoc) !void {1628fn resolveGlobalSymbol(self: *MachO, current: SymbolWithLoc) !void {
1461 const gpa = self.base.allocator;1629 const gpa = self.base.allocator;
1462 const sym = self.getSymbol(current);1630 const sym = self.getSymbol(current);
...@@ -1470,6 +1638,7 @@ fn resolveGlobalSymbol(self: *MachO, current: SymbolWithLoc) !void {...@@ -1470,6 +1638,7 @@ fn resolveGlobalSymbol(self: *MachO, current: SymbolWithLoc) !void {
1470 }1638 }
1471 return;1639 return;
1472 }1640 }
1641 const global_index = self.getGlobalIndex(sym_name).?;
1473 const global = gop.value_ptr.*;1642 const global = gop.value_ptr.*;
1474 const global_sym = self.getSymbol(global);1643 const global_sym = self.getSymbol(global);
14751644
...@@ -1500,7 +1669,20 @@ fn resolveGlobalSymbol(self: *MachO, current: SymbolWithLoc) !void {...@@ -1500,7 +1669,20 @@ fn resolveGlobalSymbol(self: *MachO, current: SymbolWithLoc) !void {
1500 const sym_is_weak = sym.sect() and (sym.weakDef() or sym.pext());1669 const sym_is_weak = sym.sect() and (sym.weakDef() or sym.pext());
1501 const global_is_weak = global_sym.sect() and (global_sym.weakDef() or global_sym.pext());1670 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
1504 if (global_is_strong) return;1686 if (global_is_strong) return;
1505 if (sym_is_weak and global_is_weak) return;1687 if (sym_is_weak and global_is_weak) return;
1506 if (sym.tentative() and global_sym.tentative()) {1688 if (sym.tentative() and global_sym.tentative()) {
...@@ -1508,11 +1690,82 @@ fn resolveGlobalSymbol(self: *MachO, current: SymbolWithLoc) !void {...@@ -1508,11 +1690,82 @@ fn resolveGlobalSymbol(self: *MachO, current: SymbolWithLoc) !void {
1508 }1690 }
1509 if (sym.undf() and !sym.tentative()) return;1691 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
1513 gop.value_ptr.* = current;1699 gop.value_ptr.* = current;
1514}1700}
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
1516fn resolveSymbolsInDylibs(self: *MachO, actions: *std.ArrayList(ResolveAction)) !void {1769fn resolveSymbolsInDylibs(self: *MachO, actions: *std.ArrayList(ResolveAction)) !void {
1517 if (self.dylibs.items.len == 0) return;1770 if (self.dylibs.items.len == 0) return;
15181771
...@@ -1542,6 +1795,7 @@ fn resolveSymbolsInDylibs(self: *MachO, actions: *std.ArrayList(ResolveAction))...@@ -1542,6 +1795,7 @@ fn resolveSymbolsInDylibs(self: *MachO, actions: *std.ArrayList(ResolveAction))
15421795
1543 if (self.unresolved.fetchSwapRemove(global_index)) |entry| blk: {1796 if (self.unresolved.fetchSwapRemove(global_index)) |entry| blk: {
1544 if (!sym.undf()) break :blk;1797 if (!sym.undf()) break :blk;
1798 if (self.mode == .zld) break :blk;
1545 try actions.append(.{ .kind = entry.value, .target = global });1799 try actions.append(.{ .kind = entry.value, .target = global });
1546 }1800 }
15471801
...@@ -1552,6 +1806,42 @@ fn resolveSymbolsInDylibs(self: *MachO, actions: *std.ArrayList(ResolveAction))...@@ -1552,6 +1806,42 @@ fn resolveSymbolsInDylibs(self: *MachO, actions: *std.ArrayList(ResolveAction))
1552 }1806 }
1553}1807}
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
1555pub fn deinit(self: *MachO) void {1845pub fn deinit(self: *MachO) void {
1556 const gpa = self.base.allocator;1846 const gpa = self.base.allocator;
15571847
...@@ -1563,8 +1853,15 @@ pub fn deinit(self: *MachO) void {...@@ -1563,8 +1853,15 @@ pub fn deinit(self: *MachO) void {
15631853
1564 self.got_table.deinit(gpa);1854 self.got_table.deinit(gpa);
1565 self.stub_table.deinit(gpa);1855 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);
1568 self.locals.deinit(gpa);1865 self.locals.deinit(gpa);
1569 self.globals.deinit(gpa);1866 self.globals.deinit(gpa);
1570 self.locals_free_list.deinit(gpa);1867 self.locals_free_list.deinit(gpa);
...@@ -1579,6 +1876,14 @@ pub fn deinit(self: *MachO) void {...@@ -1579,6 +1876,14 @@ pub fn deinit(self: *MachO) void {
1579 self.resolver.deinit(gpa);1876 self.resolver.deinit(gpa);
1580 }1877 }
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);
1582 for (self.dylibs.items) |*dylib| {1887 for (self.dylibs.items) |*dylib| {
1583 dylib.deinit(gpa);1888 dylib.deinit(gpa);
1584 }1889 }
...@@ -1623,6 +1928,11 @@ pub fn deinit(self: *MachO) void {...@@ -1623,6 +1928,11 @@ pub fn deinit(self: *MachO) void {
1623 bindings.deinit(gpa);1928 bindings.deinit(gpa);
1624 }1929 }
1625 self.bindings.deinit(gpa);1930 self.bindings.deinit(gpa);
1931
1932 for (self.misc_errors.items) |*err| {
1933 err.deinit(gpa);
1934 }
1935 self.misc_errors.deinit(gpa);
1626}1936}
16271937
1628fn freeAtom(self: *MachO, atom_index: Atom.Index) void {1938fn 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:...@@ -1717,7 +2027,7 @@ fn growAtom(self: *MachO, atom_index: Atom.Index, new_atom_size: u64, alignment:
1717 return self.allocateAtom(atom_index, new_atom_size, alignment);2027 return self.allocateAtom(atom_index, new_atom_size, alignment);
1718}2028}
17192029
1720fn allocateSymbol(self: *MachO) !u32 {2030pub fn allocateSymbol(self: *MachO) !u32 {
1721 try self.locals.ensureUnusedCapacity(self.base.allocator, 1);2031 try self.locals.ensureUnusedCapacity(self.base.allocator, 1);
17222032
1723 const index = blk: {2033 const index = blk: {
...@@ -1758,31 +2068,63 @@ fn allocateGlobal(self: *MachO) !u32 {...@@ -1758,31 +2068,63 @@ fn allocateGlobal(self: *MachO) !u32 {
1758 }2068 }
1759 };2069 };
17602070
1761 self.globals.items[index] = .{2071 self.globals.items[index] = .{ .sym_index = 0 };
1762 .sym_index = 0,
1763 .file = null,
1764 };
17652072
1766 return index;2073 return index;
1767}2074}
17682075
1769fn addGotEntry(self: *MachO, target: SymbolWithLoc) !void {2076pub fn addGotEntry(self: *MachO, target: SymbolWithLoc) !void {
1770 if (self.got_table.lookup.contains(target)) return;2077 if (self.got_table.lookup.contains(target)) return;
1771 const got_index = try self.got_table.allocateEntry(self.base.allocator, target);2078 const got_index = try self.got_table.allocateEntry(self.base.allocator, target);
1772 try self.writeOffsetTableEntry(got_index);2079 if (self.got_section_index == null) {
1773 self.got_table_count_dirty = true;2080 self.got_section_index = try self.initSection("__DATA_CONST", "__got", .{
1774 self.markRelocsDirtyByTarget(target);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 }
1775}2089}
17762090
1777fn addStubEntry(self: *MachO, target: SymbolWithLoc) !void {2091pub fn addStubEntry(self: *MachO, target: SymbolWithLoc) !void {
1778 if (self.stub_table.lookup.contains(target)) return;2092 if (self.stub_table.lookup.contains(target)) return;
1779 const stub_index = try self.stub_table.allocateEntry(self.base.allocator, target);2093 const stub_index = try self.stub_table.allocateEntry(self.base.allocator, target);
1780 try self.writeStubTableEntry(stub_index);2094 if (self.stubs_section_index == null) {
1781 self.stub_table_count_dirty = true;2095 self.stubs_section_index = try self.initSection("__TEXT", "__stubs", .{
1782 self.markRelocsDirtyByTarget(target);2096 .flags = macho.S_SYMBOL_STUBS |
1783}2097 macho.S_ATTR_PURE_INSTRUCTIONS |
17842098 macho.S_ATTR_SOME_INSTRUCTIONS,
1785pub fn updateFunc(self: *MachO, mod: *Module, func_index: InternPool.Index, air: Air, liveness: Liveness) !void {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 {
1786 if (build_options.skip_non_native and builtin.object_format != .macho) {2128 if (build_options.skip_non_native and builtin.object_format != .macho) {
1787 @panic("Attempted to compile for object format that was disabled by build configuration");2129 @panic("Attempted to compile for object format that was disabled by build configuration");
1788 }2130 }
...@@ -1866,7 +2208,9 @@ pub fn lowerUnnamedConst(self: *MachO, typed_value: TypedValue, decl_index: Modu...@@ -1866,7 +2208,9 @@ pub fn lowerUnnamedConst(self: *MachO, typed_value: TypedValue, decl_index: Modu
18662208
1867 log.debug("allocating symbol indexes for {s}", .{name});2209 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
1871 const res = try codegen.generateSymbol(&self.base, decl.srcLoc(mod), typed_value, &code_buffer, .none, .{2215 const res = try codegen.generateSymbol(&self.base, decl.srcLoc(mod), typed_value, &code_buffer, .none, .{
1872 .parent_atom_index = self.getAtom(atom_index).getSymbolIndex().?,2216 .parent_atom_index = self.getAtom(atom_index).getSymbolIndex().?,
...@@ -1876,7 +2220,7 @@ pub fn lowerUnnamedConst(self: *MachO, typed_value: TypedValue, decl_index: Modu...@@ -1876,7 +2220,7 @@ pub fn lowerUnnamedConst(self: *MachO, typed_value: TypedValue, decl_index: Modu
1876 .fail => |em| {2220 .fail => |em| {
1877 decl.analysis = .codegen_failure;2221 decl.analysis = .codegen_failure;
1878 try mod.failed_decls.put(mod.gpa, decl_index, em);2222 try mod.failed_decls.put(mod.gpa, decl_index, em);
1879 log.err("{s}", .{em.msg});2223 log.debug("{s}", .{em.msg});
1880 return error.CodegenFail;2224 return error.CodegenFail;
1881 },2225 },
1882 };2226 };
...@@ -2031,7 +2375,7 @@ fn updateLazySymbolAtom(...@@ -2031,7 +2375,7 @@ fn updateLazySymbolAtom(
2031 const code = switch (res) {2375 const code = switch (res) {
2032 .ok => code_buffer.items,2376 .ok => code_buffer.items,
2033 .fail => |em| {2377 .fail => |em| {
2034 log.err("{s}", .{em.msg});2378 log.debug("{s}", .{em.msg});
2035 return error.CodegenFail;2379 return error.CodegenFail;
2036 },2380 },
2037 };2381 };
...@@ -2068,7 +2412,11 @@ pub fn getOrCreateAtomForLazySymbol(self: *MachO, sym: File.LazySymbol) !Atom.In...@@ -2068,7 +2412,11 @@ pub fn getOrCreateAtomForLazySymbol(self: *MachO, sym: File.LazySymbol) !Atom.In
2068 },2412 },
2069 };2413 };
2070 switch (metadata.state.*) {2414 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 },
2072 .pending_flush => return metadata.atom.*,2420 .pending_flush => return metadata.atom.*,
2073 .flushed => {},2421 .flushed => {},
2074 }2422 }
...@@ -2180,8 +2528,11 @@ fn updateThreadlocalVariable(self: *MachO, module: *Module, decl_index: Module.D...@@ -2180,8 +2528,11 @@ fn updateThreadlocalVariable(self: *MachO, module: *Module, decl_index: Module.D
2180pub fn getOrCreateAtomForDecl(self: *MachO, decl_index: Module.Decl.Index) !Atom.Index {2528pub fn getOrCreateAtomForDecl(self: *MachO, decl_index: Module.Decl.Index) !Atom.Index {
2181 const gop = try self.decls.getOrPut(self.base.allocator, decl_index);2529 const gop = try self.decls.getOrPut(self.base.allocator, decl_index);
2182 if (!gop.found_existing) {2530 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);
2183 gop.value_ptr.* = .{2534 gop.value_ptr.* = .{
2184 .atom = try self.createAtom(),2535 .atom = atom_index,
2185 .section = self.getDeclOutputSection(decl_index),2536 .section = self.getDeclOutputSection(decl_index),
2186 .exports = .{},2537 .exports = .{},
2187 };2538 };
...@@ -2373,7 +2724,7 @@ pub fn updateDeclExports(...@@ -2373,7 +2724,7 @@ pub fn updateDeclExports(
2373 try decl_metadata.exports.append(gpa, sym_index);2724 try decl_metadata.exports.append(gpa, sym_index);
2374 break :blk sym_index;2725 break :blk sym_index;
2375 };2726 };
2376 const sym_loc = SymbolWithLoc{ .sym_index = sym_index, .file = null };2727 const sym_loc = SymbolWithLoc{ .sym_index = sym_index };
2377 const sym = self.getSymbolPtr(sym_loc);2728 const sym = self.getSymbolPtr(sym_loc);
2378 sym.* = .{2729 sym.* = .{
2379 .n_strx = try self.strtab.insert(gpa, exp_name),2730 .n_strx = try self.strtab.insert(gpa, exp_name),
...@@ -2403,7 +2754,7 @@ pub fn updateDeclExports(...@@ -2403,7 +2754,7 @@ pub fn updateDeclExports(
2403 error.MultipleSymbolDefinitions => {2754 error.MultipleSymbolDefinitions => {
2404 // TODO: this needs rethinking2755 // TODO: this needs rethinking
2405 const global = self.getGlobal(exp_name).?;2756 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) {
2407 _ = try mod.failed_exports.put(mod.gpa, exp, try Module.ErrorMsg.create(2758 _ = try mod.failed_exports.put(mod.gpa, exp, try Module.ErrorMsg.create(
2408 gpa,2759 gpa,
2409 decl.srcLoc(mod),2760 decl.srcLoc(mod),
...@@ -2432,7 +2783,7 @@ pub fn deleteDeclExport(...@@ -2432,7 +2783,7 @@ pub fn deleteDeclExport(
2432 defer gpa.free(exp_name);2783 defer gpa.free(exp_name);
2433 const sym_index = metadata.getExportPtr(self, exp_name) orelse return;2784 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.* };
2436 const sym = self.getSymbolPtr(sym_loc);2787 const sym = self.getSymbolPtr(sym_loc);
2437 log.debug("deleting export '{s}'", .{exp_name});2788 log.debug("deleting export '{s}'", .{exp_name});
2438 assert(sym.sect() and sym.ext());2789 assert(sym.sect() and sym.ext());
...@@ -2448,10 +2799,7 @@ pub fn deleteDeclExport(...@@ -2448,10 +2799,7 @@ pub fn deleteDeclExport(
2448 if (self.resolver.fetchRemove(exp_name)) |entry| {2799 if (self.resolver.fetchRemove(exp_name)) |entry| {
2449 defer gpa.free(entry.key);2800 defer gpa.free(entry.key);
2450 self.globals_free_list.append(gpa, entry.value) catch {};2801 self.globals_free_list.append(gpa, entry.value) catch {};
2451 self.globals.items[entry.value] = .{2802 self.globals.items[entry.value] = .{ .sym_index = 0 };
2452 .sym_index = 0,
2453 .file = null,
2454 };
2455 }2803 }
24562804
2457 sym_index.* = 0;2805 sym_index.* = 0;
...@@ -2490,10 +2838,10 @@ pub fn getDeclVAddr(self: *MachO, decl_index: Module.Decl.Index, reloc_info: Fil...@@ -2490,10 +2838,10 @@ pub fn getDeclVAddr(self: *MachO, decl_index: Module.Decl.Index, reloc_info: Fil
24902838
2491 const this_atom_index = try self.getOrCreateAtomForDecl(decl_index);2839 const this_atom_index = try self.getOrCreateAtomForDecl(decl_index);
2492 const sym_index = self.getAtom(this_atom_index).getSymbolIndex().?;2840 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 }).?;
2494 try Atom.addRelocation(self, atom_index, .{2842 try Atom.addRelocation(self, atom_index, .{
2495 .type = .unsigned,2843 .type = .unsigned,
2496 .target = .{ .sym_index = sym_index, .file = null },2844 .target = .{ .sym_index = sym_index },
2497 .offset = @as(u32, @intCast(reloc_info.offset)),2845 .offset = @as(u32, @intCast(reloc_info.offset)),
2498 .addend = reloc_info.addend,2846 .addend = reloc_info.addend,
2499 .pcrel = false,2847 .pcrel = false,
...@@ -2526,7 +2874,7 @@ fn populateMissingMetadata(self: *MachO) !void {...@@ -2526,7 +2874,7 @@ fn populateMissingMetadata(self: *MachO) !void {
2526 // The first __TEXT segment is immovable and covers MachO header and load commands.2874 // The first __TEXT segment is immovable and covers MachO header and load commands.
2527 self.header_segment_cmd_index = @as(u8, @intCast(self.segments.items.len));2875 self.header_segment_cmd_index = @as(u8, @intCast(self.segments.items.len));
2528 const ideal_size = @max(self.base.options.headerpad_size orelse 0, default_headerpad_size);2876 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
2531 log.debug("found __TEXT segment (header-only) free space 0x{x} to 0x{x}", .{ 0, needed_size });2879 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 {...@@ -2558,14 +2906,10 @@ fn populateMissingMetadata(self: *MachO) !void {
2558 }2906 }
25592907
2560 if (self.stubs_section_index == null) {2908 if (self.stubs_section_index == null) {
2561 const stub_size = stubs.calcStubEntrySize(cpu_arch);2909 const stub_size = stubs.stubSize(cpu_arch);
2562 self.stubs_section_index = try self.allocateSection("__TEXT2", "__stubs", .{2910 self.stubs_section_index = try self.allocateSection("__TEXT2", "__stubs", .{
2563 .size = stub_size,2911 .size = stub_size,
2564 .alignment = switch (cpu_arch) {2912 .alignment = stubs.stubAlignment(cpu_arch),
2565 .x86_64 => 1,
2566 .aarch64 => @sizeOf(u32),
2567 else => unreachable, // unhandled architecture type
2568 },
2569 .flags = macho.S_SYMBOL_STUBS | macho.S_ATTR_PURE_INSTRUCTIONS | macho.S_ATTR_SOME_INSTRUCTIONS,2913 .flags = macho.S_SYMBOL_STUBS | macho.S_ATTR_PURE_INSTRUCTIONS | macho.S_ATTR_SOME_INSTRUCTIONS,
2570 .reserved2 = stub_size,2914 .reserved2 = stub_size,
2571 .prot = macho.PROT.READ | macho.PROT.EXEC,2915 .prot = macho.PROT.READ | macho.PROT.EXEC,
...@@ -2576,11 +2920,7 @@ fn populateMissingMetadata(self: *MachO) !void {...@@ -2576,11 +2920,7 @@ fn populateMissingMetadata(self: *MachO) !void {
2576 if (self.stub_helper_section_index == null) {2920 if (self.stub_helper_section_index == null) {
2577 self.stub_helper_section_index = try self.allocateSection("__TEXT3", "__stub_helper", .{2921 self.stub_helper_section_index = try self.allocateSection("__TEXT3", "__stub_helper", .{
2578 .size = @sizeOf(u32),2922 .size = @sizeOf(u32),
2579 .alignment = switch (cpu_arch) {2923 .alignment = stubs.stubAlignment(cpu_arch),
2580 .x86_64 => 1,
2581 .aarch64 => @sizeOf(u32),
2582 else => unreachable, // unhandled architecture type
2583 },
2584 .flags = macho.S_REGULAR | macho.S_ATTR_PURE_INSTRUCTIONS | macho.S_ATTR_SOME_INSTRUCTIONS,2924 .flags = macho.S_REGULAR | macho.S_ATTR_PURE_INSTRUCTIONS | macho.S_ATTR_SOME_INSTRUCTIONS,
2585 .prot = macho.PROT.READ | macho.PROT.EXEC,2925 .prot = macho.PROT.READ | macho.PROT.EXEC,
2586 });2926 });
...@@ -2663,7 +3003,8 @@ fn populateMissingMetadata(self: *MachO) !void {...@@ -2663,7 +3003,8 @@ fn populateMissingMetadata(self: *MachO) !void {
26633003
2664fn calcPagezeroSize(self: *MachO) u64 {3004fn calcPagezeroSize(self: *MachO) u64 {
2665 const pagezero_vmsize = self.base.options.pagezero_size orelse default_pagezero_vmsize;3005 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);
2667 if (self.base.options.output_mode == .Lib) return 0;3008 if (self.base.options.output_mode == .Lib) return 0;
2668 if (aligned_pagezero_vmsize == 0) return 0;3009 if (aligned_pagezero_vmsize == 0) return 0;
2669 if (aligned_pagezero_vmsize != pagezero_vmsize) {3010 if (aligned_pagezero_vmsize != pagezero_vmsize) {
...@@ -2673,6 +3014,28 @@ fn calcPagezeroSize(self: *MachO) u64 {...@@ -2673,6 +3014,28 @@ fn calcPagezeroSize(self: *MachO) u64 {
2673 return aligned_pagezero_vmsize;3014 return aligned_pagezero_vmsize;
2674}3015}
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
2676fn allocateSection(self: *MachO, segname: []const u8, sectname: []const u8, opts: struct {3039fn allocateSection(self: *MachO, segname: []const u8, sectname: []const u8, opts: struct {
2677 size: u64 = 0,3040 size: u64 = 0,
2678 alignment: u32 = 0,3041 alignment: u32 = 0,
...@@ -2681,17 +3044,17 @@ fn allocateSection(self: *MachO, segname: []const u8, sectname: []const u8, opts...@@ -2681,17 +3044,17 @@ fn allocateSection(self: *MachO, segname: []const u8, sectname: []const u8, opts
2681 reserved2: u32 = 0,3044 reserved2: u32 = 0,
2682}) !u8 {3045}) !u8 {
2683 const gpa = self.base.allocator;3046 const gpa = self.base.allocator;
3047 const page_size = getPageSize(self.base.options.target.cpu.arch);
2684 // In incremental context, we create one section per segment pairing. This way,3048 // In incremental context, we create one section per segment pairing. This way,
2685 // we can move the segment in raw file as we please.3049 // we can move the segment in raw file as we please.
2686 const segment_id = @as(u8, @intCast(self.segments.items.len));3050 const segment_id = @as(u8, @intCast(self.segments.items.len));
2687 const section_id = @as(u8, @intCast(self.sections.slice().len));
2688 const vmaddr = blk: {3051 const vmaddr = blk: {
2689 const prev_segment = self.segments.items[segment_id - 1];3052 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);
2691 };3054 };
2692 // We commit more memory than needed upfront so that we don't have to reallocate too soon.3055 // 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);3056 const vmsize = mem.alignForward(u64, opts.size, page_size);
2694 const off = self.findFreeSpace(opts.size, self.page_size);3057 const off = self.findFreeSpace(opts.size, page_size);
26953058
2696 log.debug("found {s},{s} free space 0x{x} to 0x{x} (0x{x} - 0x{x})", .{3059 log.debug("found {s},{s} free space 0x{x} to 0x{x} (0x{x} - 0x{x})", .{
2697 segname,3060 segname,
...@@ -2715,23 +3078,19 @@ fn allocateSection(self: *MachO, segname: []const u8, sectname: []const u8, opts...@@ -2715,23 +3078,19 @@ fn allocateSection(self: *MachO, segname: []const u8, sectname: []const u8, opts
2715 .cmdsize = @sizeOf(macho.segment_command_64) + @sizeOf(macho.section_64),3078 .cmdsize = @sizeOf(macho.segment_command_64) + @sizeOf(macho.section_64),
2716 };3079 };
27173080
2718 var section = macho.section_64{3081 const sect_id = try self.initSection(segname, sectname, .{
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),
2725 .flags = opts.flags,3082 .flags = opts.flags,
2726 .reserved2 = opts.reserved2,3083 .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;
2728 assert(!section.isZerofill()); // TODO zerofill sections3091 assert(!section.isZerofill()); // TODO zerofill sections
27293092
2730 try self.sections.append(gpa, .{3093 return sect_id;
2731 .segment_index = segment_id,
2732 .header = section,
2733 });
2734 return section_id;
2735}3094}
27363095
2737fn growSection(self: *MachO, sect_id: u8, needed_size: u64) !void {3096fn 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 {...@@ -2740,9 +3099,10 @@ fn growSection(self: *MachO, sect_id: u8, needed_size: u64) !void {
2740 const segment = &self.segments.items[segment_index];3099 const segment = &self.segments.items[segment_index];
2741 const maybe_last_atom_index = self.sections.items(.last_atom_index)[sect_id];3100 const maybe_last_atom_index = self.sections.items(.last_atom_index)[sect_id];
2742 const sect_capacity = self.allocatedSize(header.offset);3101 const sect_capacity = self.allocatedSize(header.offset);
3102 const page_size = getPageSize(self.base.options.target.cpu.arch);
27433103
2744 if (needed_size > sect_capacity) {3104 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);
2746 const current_size = if (maybe_last_atom_index) |last_atom_index| blk: {3106 const current_size = if (maybe_last_atom_index) |last_atom_index| blk: {
2747 const last_atom = self.getAtom(last_atom_index);3107 const last_atom = self.getAtom(last_atom_index);
2748 const sym = last_atom.getSymbol(self);3108 const sym = last_atom.getSymbol(self);
...@@ -2774,16 +3134,17 @@ fn growSection(self: *MachO, sect_id: u8, needed_size: u64) !void {...@@ -2774,16 +3134,17 @@ fn growSection(self: *MachO, sect_id: u8, needed_size: u64) !void {
2774 }3134 }
27753135
2776 header.size = needed_size;3136 header.size = needed_size;
2777 segment.filesize = mem.alignForward(u64, needed_size, self.page_size);3137 segment.filesize = mem.alignForward(u64, needed_size, page_size);
2778 segment.vmsize = mem.alignForward(u64, needed_size, self.page_size);3138 segment.vmsize = mem.alignForward(u64, needed_size, page_size);
2779}3139}
27803140
2781fn growSectionVirtualMemory(self: *MachO, sect_id: u8, needed_size: u64) !void {3141fn growSectionVirtualMemory(self: *MachO, sect_id: u8, needed_size: u64) !void {
3142 const page_size = getPageSize(self.base.options.target.cpu.arch);
2782 const header = &self.sections.items(.header)[sect_id];3143 const header = &self.sections.items(.header)[sect_id];
2783 const segment = self.getSegmentPtr(sect_id);3144 const segment = self.getSegmentPtr(sect_id);
2784 const increased_size = padToIdeal(needed_size);3145 const increased_size = padToIdeal(needed_size);
2785 const old_aligned_end = segment.vmaddr + segment.vmsize;3146 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);
2787 const diff = new_aligned_end - old_aligned_end;3148 const diff = new_aligned_end - old_aligned_end;
2788 log.debug("shifting every segment after {s},{s} in virtual memory by {x}", .{3149 log.debug("shifting every segment after {s},{s} in virtual memory by {x}", .{
2789 header.segName(),3150 header.segName(),
...@@ -2814,10 +3175,29 @@ fn growSectionVirtualMemory(self: *MachO, sect_id: u8, needed_size: u64) !void {...@@ -2814,10 +3175,29 @@ fn growSectionVirtualMemory(self: *MachO, sect_id: u8, needed_size: u64) !void {
2814 }3175 }
2815}3176}
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
2817fn allocateAtom(self: *MachO, atom_index: Atom.Index, new_atom_size: u64, alignment: u64) !u64 {3195fn allocateAtom(self: *MachO, atom_index: Atom.Index, new_atom_size: u64, alignment: u64) !u64 {
2818 const tracy = trace(@src());3196 const tracy = trace(@src());
2819 defer tracy.end();3197 defer tracy.end();
28203198
3199 assert(self.mode == .incremental);
3200
2821 const atom = self.getAtom(atom_index);3201 const atom = self.getAtom(atom_index);
2822 const sect_id = atom.getSymbol(self).n_sect - 1;3202 const sect_id = atom.getSymbol(self).n_sect - 1;
2823 const segment = self.getSegmentPtr(sect_id);3203 const segment = self.getSegmentPtr(sect_id);
...@@ -2944,17 +3324,35 @@ pub fn getGlobalSymbol(self: *MachO, name: []const u8, lib_name: ?[]const u8) !u...@@ -2944,17 +3324,35 @@ pub fn getGlobalSymbol(self: *MachO, name: []const u8, lib_name: ?[]const u8) !u
2944 return self.addUndefined(sym_name, .add_stub);3324 return self.addUndefined(sym_name, .add_stub);
2945}3325}
29463326
2947fn writeSegmentHeaders(self: *MachO, writer: anytype) !void {3327pub fn writeSegmentHeaders(self: *MachO, writer: anytype) !void {
2948 for (self.segments.items, 0..) |seg, i| {3328 for (self.segments.items, 0..) |seg, i| {
2949 const indexes = self.getSectionIndexes(@as(u8, @intCast(i)));3329 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);
2951 for (self.sections.items(.header)[indexes.start..indexes.end]) |header| {3347 for (self.sections.items(.header)[indexes.start..indexes.end]) |header| {
3348 if (header.size == 0) continue;
2952 try writer.writeStruct(header);3349 try writer.writeStruct(header);
2953 }3350 }
2954 }3351 }
2955}3352}
29563353
2957fn writeLinkeditSegmentData(self: *MachO) !void {3354pub fn writeLinkeditSegmentData(self: *MachO) !void {
3355 const page_size = getPageSize(self.base.options.target.cpu.arch);
2958 const seg = self.getLinkeditSegmentPtr();3356 const seg = self.getLinkeditSegmentPtr();
2959 seg.filesize = 0;3357 seg.filesize = 0;
2960 seg.vmsize = 0;3358 seg.vmsize = 0;
...@@ -2962,27 +3360,33 @@ fn writeLinkeditSegmentData(self: *MachO) !void {...@@ -2962,27 +3360,33 @@ fn writeLinkeditSegmentData(self: *MachO) !void {
2962 for (self.segments.items, 0..) |segment, id| {3360 for (self.segments.items, 0..) |segment, id| {
2963 if (self.linkedit_segment_cmd_index.? == @as(u8, @intCast(id))) continue;3361 if (self.linkedit_segment_cmd_index.? == @as(u8, @intCast(id))) continue;
2964 if (seg.vmaddr < segment.vmaddr + segment.vmsize) {3362 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);
2966 }3364 }
2967 if (seg.fileoff < segment.fileoff + segment.filesize) {3365 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);
2969 }3367 }
2970 }3368 }
29713369
2972 try self.writeDyldInfoData();3370 try self.writeDyldInfoData();
3371 // TODO handle this better
3372 if (self.mode == .zld) {
3373 try self.writeFunctionStarts();
3374 try self.writeDataInCode();
3375 }
2973 try self.writeSymtabs();3376 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);
2976}3379}
29773380
2978fn collectRebaseDataFromTableSection(self: *MachO, sect_id: u8, rebase: *Rebase, table: anytype) !void {3381fn collectRebaseDataFromTableSection(self: *MachO, sect_id: u8, rebase: *Rebase, table: anytype) !void {
3382 const gpa = self.base.allocator;
2979 const header = self.sections.items(.header)[sect_id];3383 const header = self.sections.items(.header)[sect_id];
2980 const segment_index = self.sections.items(.segment_index)[sect_id];3384 const segment_index = self.sections.items(.segment_index)[sect_id];
2981 const segment = self.segments.items[segment_index];3385 const segment = self.segments.items[segment_index];
2982 const base_offset = header.addr - segment.vmaddr;3386 const base_offset = header.addr - segment.vmaddr;
2983 const is_got = if (self.got_section_index) |index| index == sect_id else false;3387 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
2987 for (table.entries.items, 0..) |entry, i| {3391 for (table.entries.items, 0..) |entry, i| {
2988 if (!table.lookup.contains(entry)) continue;3392 if (!table.lookup.contains(entry)) continue;
...@@ -3024,19 +3428,96 @@ fn collectRebaseData(self: *MachO, rebase: *Rebase) !void {...@@ -3024,19 +3428,96 @@ fn collectRebaseData(self: *MachO, rebase: *Rebase) !void {
3024 }3428 }
3025 }3429 }
30263430
3027 try self.collectRebaseDataFromTableSection(self.got_section_index.?, rebase, self.got_table);3431 // Unpack GOT entries
3028 try self.collectRebaseDataFromTableSection(self.la_symbol_ptr_section_index.?, rebase, self.stub_table);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
3030 try rebase.finalize(gpa);3510 try rebase.finalize(gpa);
3031}3511}
30323512
3033fn collectBindDataFromTableSection(self: *MachO, sect_id: u8, bind: anytype, table: anytype) !void {3513fn collectBindDataFromTableSection(self: *MachO, sect_id: u8, bind: anytype, table: anytype) !void {
3514 const gpa = self.base.allocator;
3034 const header = self.sections.items(.header)[sect_id];3515 const header = self.sections.items(.header)[sect_id];
3035 const segment_index = self.sections.items(.segment_index)[sect_id];3516 const segment_index = self.sections.items(.segment_index)[sect_id];
3036 const segment = self.segments.items[segment_index];3517 const segment = self.segments.items[segment_index];
3037 const base_offset = header.addr - segment.vmaddr;3518 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
3041 for (table.entries.items, 0..) |entry, i| {3522 for (table.entries.items, 0..) |entry, i| {
3042 if (!table.lookup.contains(entry)) continue;3523 if (!table.lookup.contains(entry)) continue;
...@@ -3101,13 +3582,105 @@ fn collectBindData(self: *MachO, bind: anytype, raw_bindings: anytype) !void {...@@ -3101,13 +3582,105 @@ fn collectBindData(self: *MachO, bind: anytype, raw_bindings: anytype) !void {
3101 }3582 }
3102 }3583 }
31033584
3104 // Gather GOT pointers3585 // Unpack GOT pointers
3105 try self.collectBindDataFromTableSection(self.got_section_index.?, bind, self.got_table);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
3106 try bind.finalize(gpa, self);3678 try bind.finalize(gpa, self);
3107}3679}
31083680
3109fn collectLazyBindData(self: *MachO, bind: anytype) !void {3681fn 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);
3111 try bind.finalize(self.base.allocator, self);3684 try bind.finalize(self.base.allocator, self);
3112}3685}
31133686
...@@ -3124,7 +3697,8 @@ fn collectExportData(self: *MachO, trie: *Trie) !void {...@@ -3124,7 +3697,8 @@ fn collectExportData(self: *MachO, trie: *Trie) !void {
3124 const sym = self.getSymbol(global);3697 const sym = self.getSymbol(global);
31253698
3126 if (sym.undf()) continue;3699 if (sym.undf()) continue;
3127 if (!sym.ext()) continue;3700 assert(sym.ext());
3701 if (sym.n_desc == N_DEAD) continue;
31283702
3129 const sym_name = self.getSymbolName(global);3703 const sym_name = self.getSymbolName(global);
3130 log.debug(" (putting '{s}' defined at 0x{x})", .{ sym_name, sym.n_value });3704 log.debug(" (putting '{s}' defined at 0x{x})", .{ sym_name, sym.n_value });
...@@ -3227,18 +3801,18 @@ fn writeDyldInfoData(self: *MachO) !void {...@@ -3227,18 +3801,18 @@ fn writeDyldInfoData(self: *MachO) !void {
3227 self.dyld_info_cmd.export_size = @as(u32, @intCast(export_size_aligned));3801 self.dyld_info_cmd.export_size = @as(u32, @intCast(export_size_aligned));
3228}3802}
32293803
3230fn populateLazyBindOffsetsInStubHelper(self: *MachO, lazy_bind: LazyBind) !void {3804fn populateLazyBindOffsetsInStubHelper(self: *MachO, lazy_bind: anytype) !void {
3231 if (lazy_bind.size() == 0) return;3805 if (lazy_bind.size() == 0) return;
32323806
3233 const stub_helper_section_index = self.stub_helper_section_index.?;3807 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
3236 const header = self.sections.items(.header)[stub_helper_section_index];3810 const header = self.sections.items(.header)[stub_helper_section_index];
32373811
3238 const cpu_arch = self.base.options.target.cpu.arch;3812 const cpu_arch = self.base.options.target.cpu.arch;
3239 const preamble_size = stubs.calcStubHelperPreambleSize(cpu_arch);3813 const preamble_size = stubs.stubHelperPreambleSize(cpu_arch);
3240 const stub_size = stubs.calcStubHelperEntrySize(cpu_arch);3814 const stub_size = stubs.stubHelperSize(cpu_arch);
3241 const stub_offset = stubs.calcStubOffsetInStubHelper(cpu_arch);3815 const stub_offset = stubs.stubOffsetInStubHelper(cpu_arch);
3242 const base_offset = header.offset + preamble_size;3816 const base_offset = header.offset + preamble_size;
32433817
3244 for (lazy_bind.offsets.items, 0..) |bind_offset, index| {3818 for (lazy_bind.offsets.items, 0..) |bind_offset, index| {
...@@ -3254,6 +3828,167 @@ fn populateLazyBindOffsetsInStubHelper(self: *MachO, lazy_bind: LazyBind) !void...@@ -3254,6 +3828,167 @@ fn populateLazyBindOffsetsInStubHelper(self: *MachO, lazy_bind: LazyBind) !void
3254 }3828 }
3255}3829}
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
3257fn writeSymtabs(self: *MachO) !void {3992fn writeSymtabs(self: *MachO) !void {
3258 var ctx = try self.writeSymtab();3993 var ctx = try self.writeSymtab();
3259 defer ctx.imports_table.deinit();3994 defer ctx.imports_table.deinit();
...@@ -3261,18 +3996,38 @@ fn writeSymtabs(self: *MachO) !void {...@@ -3261,18 +3996,38 @@ fn writeSymtabs(self: *MachO) !void {
3261 try self.writeStrtab();3996 try self.writeStrtab();
3262}3997}
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
3264fn writeSymtab(self: *MachO) !SymtabCtx {4010fn writeSymtab(self: *MachO) !SymtabCtx {
3265 const gpa = self.base.allocator;4011 const gpa = self.base.allocator;
32664012
3267 var locals = std.ArrayList(macho.nlist_64).init(gpa);4013 var locals = std.ArrayList(macho.nlist_64).init(gpa);
3268 defer locals.deinit();4014 defer locals.deinit();
32694015
3270 for (self.locals.items, 0..) |sym, sym_id| {4016 for (0..self.locals.items.len) |sym_id| {
3271 if (sym.n_strx == 0) continue; // no name, skip4017 try self.addLocalToSymtab(.{ .sym_index = @intCast(sym_id) }, &locals);
3272 const sym_loc = SymbolWithLoc{ .sym_index = @as(u32, @intCast(sym_id)), .file = null };4018 }
3273 if (self.symbolIsTemp(sym_loc)) continue; // local temp symbol, skip4019
3274 if (self.getGlobal(self.getSymbolName(sym_loc)) != null) continue; // global symbol is either an export or import, skip4020 for (self.objects.items) |object| {
3275 try locals.append(sym);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 }
3276 }4031 }
32774032
3278 var exports = std.ArrayList(macho.nlist_64).init(gpa);4033 var exports = std.ArrayList(macho.nlist_64).init(gpa);
...@@ -3281,6 +4036,7 @@ fn writeSymtab(self: *MachO) !SymtabCtx {...@@ -3281,6 +4036,7 @@ fn writeSymtab(self: *MachO) !SymtabCtx {
3281 for (self.globals.items) |global| {4036 for (self.globals.items) |global| {
3282 const sym = self.getSymbol(global);4037 const sym = self.getSymbol(global);
3283 if (sym.undf()) continue; // import, skip4038 if (sym.undf()) continue; // import, skip
4039 if (sym.n_desc == N_DEAD) continue;
3284 var out_sym = sym;4040 var out_sym = sym;
3285 out_sym.n_strx = try self.strtab.insert(gpa, self.getSymbolName(global));4041 out_sym.n_strx = try self.strtab.insert(gpa, self.getSymbolName(global));
3286 try exports.append(out_sym);4042 try exports.append(out_sym);
...@@ -3295,6 +4051,7 @@ fn writeSymtab(self: *MachO) !SymtabCtx {...@@ -3295,6 +4051,7 @@ fn writeSymtab(self: *MachO) !SymtabCtx {
3295 const sym = self.getSymbol(global);4051 const sym = self.getSymbol(global);
3296 if (sym.n_strx == 0) continue; // no name, skip4052 if (sym.n_strx == 0) continue; // no name, skip
3297 if (!sym.undf()) continue; // not an import, skip4053 if (!sym.undf()) continue; // not an import, skip
4054 if (sym.n_desc == N_DEAD) continue;
3298 const new_index = @as(u32, @intCast(imports.items.len));4055 const new_index = @as(u32, @intCast(imports.items.len));
3299 var out_sym = sym;4056 var out_sym = sym;
3300 out_sym.n_strx = try self.strtab.insert(gpa, self.getSymbolName(global));4057 out_sym.n_strx = try self.strtab.insert(gpa, self.getSymbolName(global));
...@@ -3302,6 +4059,15 @@ fn writeSymtab(self: *MachO) !SymtabCtx {...@@ -3302,6 +4059,15 @@ fn writeSymtab(self: *MachO) !SymtabCtx {
3302 try imports_table.putNoClobber(global, new_index);4059 try imports_table.putNoClobber(global, new_index);
3303 }4060 }
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
3305 const nlocals = @as(u32, @intCast(locals.items.len));4071 const nlocals = @as(u32, @intCast(locals.items.len));
3306 const nexports = @as(u32, @intCast(exports.items.len));4072 const nexports = @as(u32, @intCast(exports.items.len));
3307 const nimports = @as(u32, @intCast(imports.items.len));4073 const nimports = @as(u32, @intCast(imports.items.len));
...@@ -3335,74 +4101,297 @@ fn writeSymtab(self: *MachO) !SymtabCtx {...@@ -3335,74 +4101,297 @@ fn writeSymtab(self: *MachO) !SymtabCtx {
3335 };4101 };
3336}4102}
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
3339 const gpa = self.base.allocator;4114 const gpa = self.base.allocator;
3340 const seg = self.getLinkeditSegmentPtr();4115 var debug_info = object.parseDwarfInfo();
3341 const offset = seg.fileoff + seg.filesize;4116
3342 assert(mem.isAlignedGeneric(u64, offset, @alignOf(u64)));4117 var lookup = DwarfInfo.AbbrevLookupTable.init(gpa);
3343 const needed_size = self.strtab.buffer.items.len;4118 defer lookup.deinit();
3344 const needed_size_aligned = mem.alignForward(u64, needed_size, @alignOf(u64));4119 try lookup.ensureUnusedCapacity(std.math.maxInt(u8));
3345 seg.filesize = offset + needed_size_aligned - seg.fileoff;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);4140 const cu_entry = maybe_cu_entry orelse {
3350 defer gpa.free(buffer);4141 log.debug("missing DWARF_TAG_compile_unit tag in {s}; skipping", .{object.name});
3351 @memcpy(buffer[0..self.strtab.buffer.items.len], self.strtab.buffer.items);4142 return;
3352 @memset(buffer[self.strtab.buffer.items.len..], 0);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));4149 blk: {
3357 self.symtab_cmd.strsize = @as(u32, @intCast(needed_size_aligned));4150 while (attr_it.next() catch break :blk) |attr| switch (attr.name) {
3358}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 {4157 if (maybe_tu_name == null or maybe_tu_comp_dir == null) {
3361 nlocalsym: u32,4158 log.debug("missing DWARF_AT_comp_dir and DWARF_AT_name attributes {s}; skipping", .{object.name});
3362 nextdefsym: u32,4159 return;
3363 nundefsym: u32,4160 }
3364 imports_table: std.AutoHashMap(SymbolWithLoc, u32),
3365};
33664161
3367fn writeDysymtab(self: *MachO, ctx: SymtabCtx) !void {4162 const tu_name = maybe_tu_name.?;
3368 const gpa = self.base.allocator;4163 const tu_comp_dir = maybe_tu_comp_dir.?;
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;
33744164
3375 const seg = self.getLinkeditSegmentPtr();4165 // Open scope
3376 const offset = seg.fileoff + seg.filesize;4166 try locals.ensureUnusedCapacity(3);
3377 assert(mem.isAlignedGeneric(u64, offset, @alignOf(u64)));4167 locals.appendAssumeCapacity(.{
3378 const needed_size = nindirectsyms * @sizeOf(u32);4168 .n_strx = try self.strtab.insert(gpa, tu_comp_dir),
3379 const needed_size_aligned = mem.alignForward(u64, needed_size, @alignOf(u64));4169 .n_type = macho.N_SO,
3380 seg.filesize = offset + needed_size_aligned - seg.fileoff;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);4191 var name_lookup: ?DwarfInfo.SubprogramLookupByName = if (object.header.flags & macho.MH_SUBSECTIONS_VIA_SYMBOLS == 0) blk: {
3385 defer buf.deinit();4192 var name_lookup = DwarfInfo.SubprogramLookupByName.init(gpa);
3386 try buf.ensureTotalCapacity(math.cast(usize, needed_size_aligned) orelse return error.Overflow);4193 errdefer name_lookup.deinit();
3387 const writer = buf.writer();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| {4203 for (object.atoms.items) |atom_index| {
3390 const stubs_header = &self.sections.items(.header)[sect_id];4204 const atom = self.getAtom(atom_index);
3391 stubs_header.reserved1 = 0;4205 const stabs = try self.generateSymbolStabsForSymbol(
3392 for (self.stub_table.entries.items) |entry| {4206 atom_index,
3393 if (!self.stub_table.lookup.contains(entry)) continue;4207 atom.getSymbolWithLoc(),
3394 const target_sym = self.getSymbol(entry);4208 name_lookup,
3395 assert(target_sym.undf());4209 &stabs_buf,
3396 try writer.writeIntLittle(u32, iundefsym + ctx.imports_table.get(entry).?);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);
3397 }4222 }
3398 }4223 }
33994224
3400 if (self.got_section_index) |sect_id| {4225 // Close scope
3401 const got = &self.sections.items(.header)[sect_id];4226 try locals.append(.{
3402 got.reserved1 = nstubs;4227 .n_strx = 0,
3403 for (self.got_table.entries.items) |entry| {4228 .n_type = macho.N_SO,
3404 if (!self.got_table.lookup.contains(entry)) continue;4229 .n_sect = 0,
3405 const target_sym = self.getSymbol(entry);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);
3406 if (target_sym.undf()) {4395 if (target_sym.undf()) {
3407 try writer.writeIntLittle(u32, iundefsym + ctx.imports_table.get(entry).?);4396 try writer.writeIntLittle(u32, iundefsym + ctx.imports_table.get(entry).?);
3408 } else {4397 } else {
...@@ -3439,7 +4428,7 @@ fn writeDysymtab(self: *MachO, ctx: SymtabCtx) !void {...@@ -3439,7 +4428,7 @@ fn writeDysymtab(self: *MachO, ctx: SymtabCtx) !void {
3439 self.dysymtab_cmd.nindirectsyms = nindirectsyms;4428 self.dysymtab_cmd.nindirectsyms = nindirectsyms;
3440}4429}
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 {
3443 const file_size = if (!has_codesig) blk: {4432 const file_size = if (!has_codesig) blk: {
3444 const seg = self.getLinkeditSegmentPtr();4433 const seg = self.getLinkeditSegmentPtr();
3445 break :blk seg.fileoff + seg.filesize;4434 break :blk seg.fileoff + seg.filesize;
...@@ -3449,14 +4438,14 @@ fn writeUuid(self: *MachO, comp: *const Compilation, uuid_cmd_offset: u32, has_c...@@ -3449,14 +4438,14 @@ fn writeUuid(self: *MachO, comp: *const Compilation, uuid_cmd_offset: u32, has_c
3449 try self.base.file.?.pwriteAll(&self.uuid_cmd.uuid, offset);4438 try self.base.file.?.pwriteAll(&self.uuid_cmd.uuid, offset);
3450}4439}
34514440
3452fn writeCodeSignaturePadding(self: *MachO, code_sig: *CodeSignature) !void {4441pub fn writeCodeSignaturePadding(self: *MachO, code_sig: *CodeSignature) !void {
3453 const seg = self.getLinkeditSegmentPtr();4442 const seg = self.getLinkeditSegmentPtr();
3454 // Code signature data has to be 16-bytes aligned for Apple tools to recognize the file4443 // Code signature data has to be 16-bytes aligned for Apple tools to recognize the file
3455 // https://github.com/opensource-apple/cctools/blob/fdb4825f303fd5c0751be524babd32958181b3ed/libstuff/checkout.c#L2714444 // https://github.com/opensource-apple/cctools/blob/fdb4825f303fd5c0751be524babd32958181b3ed/libstuff/checkout.c#L271
3456 const offset = mem.alignForward(u64, seg.fileoff + seg.filesize, 16);4445 const offset = mem.alignForward(u64, seg.fileoff + seg.filesize, 16);
3457 const needed_size = code_sig.estimateSize(offset);4446 const needed_size = code_sig.estimateSize(offset);
3458 seg.filesize = offset + needed_size - seg.fileoff;4447 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));
3460 log.debug("writing code signature padding from 0x{x} to 0x{x}", .{ offset, offset + needed_size });4449 log.debug("writing code signature padding from 0x{x} to 0x{x}", .{ offset, offset + needed_size });
3461 // Pad out the space. We need to do this to calculate valid hashes for everything in the file4450 // Pad out the space. We need to do this to calculate valid hashes for everything in the file
3462 // except for code signature data.4451 // except for code signature data.
...@@ -3466,8 +4455,9 @@ fn writeCodeSignaturePadding(self: *MachO, code_sig: *CodeSignature) !void {...@@ -3466,8 +4455,9 @@ fn writeCodeSignaturePadding(self: *MachO, code_sig: *CodeSignature) !void {
3466 self.codesig_cmd.datasize = @as(u32, @intCast(needed_size));4455 self.codesig_cmd.datasize = @as(u32, @intCast(needed_size));
3467}4456}
34684457
3469fn writeCodeSignature(self: *MachO, comp: *const Compilation, code_sig: *CodeSignature) !void {4458pub fn writeCodeSignature(self: *MachO, comp: *const Compilation, code_sig: *CodeSignature) !void {
3470 const seg = self.getSegment(self.text_section_index.?);4459 const seg_id = self.header_segment_cmd_index.?;
4460 const seg = self.segments.items[seg_id];
3471 const offset = self.codesig_cmd.dataoff;4461 const offset = self.codesig_cmd.dataoff;
34724462
3473 var buffer = std.ArrayList(u8).init(self.base.allocator);4463 var buffer = std.ArrayList(u8).init(self.base.allocator);
...@@ -3491,14 +4481,10 @@ fn writeCodeSignature(self: *MachO, comp: *const Compilation, code_sig: *CodeSig...@@ -3491,14 +4481,10 @@ fn writeCodeSignature(self: *MachO, comp: *const Compilation, code_sig: *CodeSig
3491}4481}
34924482
3493/// Writes Mach-O file header.4483/// Writes Mach-O file header.
3494fn writeHeader(self: *MachO, ncmds: u32, sizeofcmds: u32) !void {4484pub fn writeHeader(self: *MachO, ncmds: u32, sizeofcmds: u32) !void {
3495 var header: macho.mach_header_64 = .{};4485 var header: macho.mach_header_64 = .{};
3496 header.flags = macho.MH_NOUNDEFS | macho.MH_DYLDLINK | macho.MH_PIE | macho.MH_TWOLEVEL;4486 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
3502 switch (self.base.options.target.cpu.arch) {4488 switch (self.base.options.target.cpu.arch) {
3503 .aarch64 => {4489 .aarch64 => {
3504 header.cputype = macho.CPU_TYPE_ARM64;4490 header.cputype = macho.CPU_TYPE_ARM64;
...@@ -3508,7 +4494,7 @@ fn writeHeader(self: *MachO, ncmds: u32, sizeofcmds: u32) !void {...@@ -3508,7 +4494,7 @@ fn writeHeader(self: *MachO, ncmds: u32, sizeofcmds: u32) !void {
3508 header.cputype = macho.CPU_TYPE_X86_64;4494 header.cputype = macho.CPU_TYPE_X86_64;
3509 header.cpusubtype = macho.CPU_SUBTYPE_X86_64_ALL;4495 header.cpusubtype = macho.CPU_SUBTYPE_X86_64_ALL;
3510 },4496 },
3511 else => return error.UnsupportedCpuArchitecture,4497 else => unreachable,
3512 }4498 }
35134499
3514 switch (self.base.options.output_mode) {4500 switch (self.base.options.output_mode) {
...@@ -3523,6 +4509,13 @@ fn writeHeader(self: *MachO, ncmds: u32, sizeofcmds: u32) !void {...@@ -3523,6 +4509,13 @@ fn writeHeader(self: *MachO, ncmds: u32, sizeofcmds: u32) !void {
3523 else => unreachable,4509 else => unreachable,
3524 }4510 }
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
3526 header.ncmds = ncmds;4519 header.ncmds = ncmds;
3527 header.sizeofcmds = sizeofcmds;4520 header.sizeofcmds = sizeofcmds;
35284521
...@@ -3618,9 +4611,7 @@ fn addUndefined(self: *MachO, name: []const u8, action: ResolveAction.Kind) !u32...@@ -3618,9 +4611,7 @@ fn addUndefined(self: *MachO, name: []const u8, action: ResolveAction.Kind) !u32
3618 const gop = try self.getOrPutGlobalPtr(name);4611 const gop = try self.getOrPutGlobalPtr(name);
3619 const global_index = self.getGlobalIndex(name).?;4612 const global_index = self.getGlobalIndex(name).?;
36204613
3621 if (gop.found_existing) {4614 if (gop.found_existing) return global_index;
3622 return global_index;
3623 }
36244615
3625 const sym_index = try self.allocateSymbol();4616 const sym_index = try self.allocateSymbol();
3626 const sym_loc = SymbolWithLoc{ .sym_index = sym_index };4617 const sym_loc = SymbolWithLoc{ .sym_index = sym_index };
...@@ -3641,7 +4632,7 @@ pub fn makeStaticString(bytes: []const u8) [16]u8 {...@@ -3641,7 +4632,7 @@ pub fn makeStaticString(bytes: []const u8) [16]u8 {
3641 return buf;4632 return buf;
3642}4633}
36434634
3644fn getSegmentByName(self: MachO, segname: []const u8) ?u8 {4635pub fn getSegmentByName(self: MachO, segname: []const u8) ?u8 {
3645 for (self.segments.items, 0..) |seg, i| {4636 for (self.segments.items, 0..) |seg, i| {
3646 if (mem.eql(u8, segname, seg.segName())) return @as(u8, @intCast(i));4637 if (mem.eql(u8, segname, seg.segName())) return @as(u8, @intCast(i));
3647 } else return null;4638 } else return null;
...@@ -3689,20 +4680,33 @@ pub fn symbolIsTemp(self: *MachO, sym_with_loc: SymbolWithLoc) bool {...@@ -3689,20 +4680,33 @@ pub fn symbolIsTemp(self: *MachO, sym_with_loc: SymbolWithLoc) bool {
36894680
3690/// Returns pointer-to-symbol described by `sym_with_loc` descriptor.4681/// Returns pointer-to-symbol described by `sym_with_loc` descriptor.
3691pub fn getSymbolPtr(self: *MachO, sym_with_loc: SymbolWithLoc) *macho.nlist_64 {4682pub fn getSymbolPtr(self: *MachO, sym_with_loc: SymbolWithLoc) *macho.nlist_64 {
3692 assert(sym_with_loc.file == null);4683 if (sym_with_loc.getFile()) |file| {
3693 return &self.locals.items[sym_with_loc.sym_index];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 }
3694}4689}
36954690
3696/// Returns symbol described by `sym_with_loc` descriptor.4691/// Returns symbol described by `sym_with_loc` descriptor.
3697pub fn getSymbol(self: *const MachO, sym_with_loc: SymbolWithLoc) macho.nlist_64 {4692pub fn getSymbol(self: *const MachO, sym_with_loc: SymbolWithLoc) macho.nlist_64 {
3698 assert(sym_with_loc.file == null);4693 if (sym_with_loc.getFile()) |file| {
3699 return self.locals.items[sym_with_loc.sym_index];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 }
3700}4699}
37014700
3702/// Returns name of the symbol described by `sym_with_loc` descriptor.4701/// Returns name of the symbol described by `sym_with_loc` descriptor.
3703pub fn getSymbolName(self: *const MachO, sym_with_loc: SymbolWithLoc) []const u8 {4702pub fn getSymbolName(self: *const MachO, sym_with_loc: SymbolWithLoc) []const u8 {
3704 const sym = self.getSymbol(sym_with_loc);4703 if (sym_with_loc.getFile()) |file| {
3705 return self.strtab.get(sym.n_strx).?;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 }
3706}4710}
37074711
3708/// Returns pointer to the global entry for `name` if one exists.4712/// 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 {...@@ -3761,18 +4765,33 @@ pub fn getAtomPtr(self: *MachO, atom_index: Atom.Index) *Atom {
3761/// Returns atom if there is an atom referenced by the symbol described by `sym_with_loc` descriptor.4765/// Returns atom if there is an atom referenced by the symbol described by `sym_with_loc` descriptor.
3762/// Returns null on failure.4766/// Returns null on failure.
3763pub fn getAtomIndexForSymbol(self: *MachO, sym_with_loc: SymbolWithLoc) ?Atom.Index {4767pub fn getAtomIndexForSymbol(self: *MachO, sym_with_loc: SymbolWithLoc) ?Atom.Index {
3764 assert(sym_with_loc.file == null);4768 assert(sym_with_loc.getFile() == null);
3765 return self.atom_by_index_table.get(sym_with_loc.sym_index);4769 return self.atom_by_index_table.get(sym_with_loc.sym_index);
3766}4770}
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.
3769/// Asserts output mode is executable.4791/// Asserts output mode is executable.
3770pub fn getEntryPoint(self: MachO) error{MissingMainEntrypoint}!SymbolWithLoc {4792pub fn getEntryPoint(self: MachO) ?SymbolWithLoc {
3771 const entry_name = self.base.options.entry orelse load_commands.default_entry_point;4793 const entry_name = self.base.options.entry orelse load_commands.default_entry_point;
3772 const global = self.getGlobal(entry_name) orelse {4794 const global = self.getGlobal(entry_name) orelse return null;
3773 log.err("entrypoint '{s}' not found", .{entry_name});
3774 return error.MissingMainEntrypoint;
3775 };
3776 return global;4795 return global;
3777}4796}
37784797
...@@ -3781,273 +4800,318 @@ pub fn getDebugSymbols(self: *MachO) ?*DebugSymbols {...@@ -3781,273 +4800,318 @@ pub fn getDebugSymbols(self: *MachO) ?*DebugSymbols {
3781 return &self.d_sym.?;4800 return &self.d_sym.?;
3782}4801}
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 {
3785 if (!@hasDecl(@TypeOf(predicate), "predicate"))5074 if (!@hasDecl(@TypeOf(predicate), "predicate"))
3786 @compileError("Predicate is required to define fn predicate(@This(), T) bool");5075 @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;
3791 while (i < haystack.len) : (i += 1) {5097 while (i < haystack.len) : (i += 1) {
3792 if (predicate.predicate(haystack[i])) break;5098 if (predicate.predicate(haystack[i])) break;
3793 }5099 }
3794 return i;5100 return i;
3795}5101}
37965102
3797// fn snapshotState(self: *MachO) !void {5103pub fn logSegments(self: *MachO) void {
3798// const emit = self.base.options.emit orelse {5104 log.debug("segments:", .{});
3799// log.debug("no emit directory found; skipping snapshot...", .{});5105 for (self.segments.items, 0..) |segment, i| {
3800// return;5106 log.debug(" segment({d}): {s} @{x} ({x}), sizeof({x})", .{
3801// };5107 i,
38025108 segment.segName(),
3803// const Snapshot = struct {5109 segment.fileoff,
3804// const Node = struct {5110 segment.vmaddr,
3805// const Tag = enum {5111 segment.vmsize,
3806// section_start,5112 });
3807// section_end,5113 }
3808// atom_start,5114}
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// }
40515115
4052pub fn logSections(self: *MachO) void {5116pub fn logSections(self: *MachO) void {
4053 log.debug("sections:", .{});5117 log.debug("sections:", .{});
...@@ -4063,9 +5127,7 @@ pub fn logSections(self: *MachO) void {...@@ -4063,9 +5127,7 @@ pub fn logSections(self: *MachO) void {
4063 }5127 }
4064}5128}
40655129
4066fn logSymAttributes(sym: macho.nlist_64, buf: *[4]u8) []const u8 {5130fn logSymAttributes(sym: macho.nlist_64, buf: []u8) []const u8 {
4067 @memset(buf[0..4], '_');
4068 @memset(buf[4..], ' ');
4069 if (sym.sect()) {5131 if (sym.sect()) {
4070 buf[0] = 's';5132 buf[0] = 's';
4071 }5133 }
...@@ -4088,56 +5150,110 @@ fn logSymAttributes(sym: macho.nlist_64, buf: *[4]u8) []const u8 {...@@ -4088,56 +5150,110 @@ fn logSymAttributes(sym: macho.nlist_64, buf: *[4]u8) []const u8 {
4088pub fn logSymtab(self: *MachO) void {5150pub fn logSymtab(self: *MachO) void {
4089 var buf: [4]u8 = undefined;5151 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)", .{});
4092 for (self.locals.items, 0..) |sym, sym_id| {5171 for (self.locals.items, 0..) |sym, sym_id| {
4093 const where = if (sym.undf() and !sym.tentative()) "ord" else "sect";5172 if (sym.undf()) continue;
4094 const def_index = if (sym.undf() and !sym.tentative())5173 scoped_log.debug(" %{d}: {s} @{x} in sect({d}), {s}", .{
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}", .{
4099 sym_id,5174 sym_id,
4100 self.strtab.get(sym.n_strx),5175 self.strtab.get(sym.n_strx).?,
4101 sym.n_value,5176 sym.n_value,
4102 where,5177 sym.n_sect,
4103 def_index,
4104 logSymAttributes(sym, &buf),5178 logSymAttributes(sym, &buf),
4105 });5179 });
4106 }5180 }
41075181
4108 log.debug("globals table:", .{});5182 scoped_log.debug("exports:", .{});
4109 for (self.globals.items) |global| {5183 for (self.globals.items, 0..) |global, i| {
4110 const name = self.getSymbolName(global);5184 const sym = self.getSymbol(global);
4111 log.debug(" {s} => %{d} in object({?d})", .{ name, global.sym_index, global.file });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 });
4112 }5195 }
41135196
4114 log.debug("GOT entries:", .{});5197 scoped_log.debug("imports:", .{});
4115 log.debug("{}", .{self.got_table});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:", .{});5212 scoped_log.debug("GOT entries:", .{});
4118 log.debug("{}", .{self.stub_table});5213 scoped_log.debug("{}", .{self.got_table});
4119}
41205214
4121pub fn logAtoms(self: *MachO) void {5215 scoped_log.debug("TLV pointers:", .{});
4122 log.debug("atoms:", .{});5216 scoped_log.debug("{}", .{self.tlv_ptr_table});
41235217
4124 const slice = self.sections.slice();5218 scoped_log.debug("stubs entries:", .{});
4125 for (slice.items(.last_atom_index), 0..) |last_atom_index, i| {5219 scoped_log.debug("{}", .{self.stub_table});
4126 var atom_index = last_atom_index orelse continue;
4127 const header = slice.items(.header)[i];
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));
4130 const atom = self.getAtom(atom_index);5227 const atom = self.getAtom(atom_index);
4131 if (atom.prev_index) |prev_index| {5228 const atom_sym = self.getSymbol(atom.getSymbolWithLoc());
4132 atom_index = prev_index;5229 const target_addr = switch (tag) {
4133 } else break;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 });
4134 }5240 }
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
4136 log.debug("{s},{s}", .{ header.segName(), header.sectName() });5251 log.debug("{s},{s}", .{ header.segName(), header.sectName() });
41375252
4138 while (true) {5253 while (true) {
4139 self.logAtom(atom_index);
4140 const atom = self.getAtom(atom_index);5254 const atom = self.getAtom(atom_index);
5255 self.logAtom(atom_index, log);
5256
4141 if (atom.next_index) |next_index| {5257 if (atom.next_index) |next_index| {
4142 atom_index = next_index;5258 atom_index = next_index;
4143 } else break;5259 } else break;
...@@ -4145,16 +5261,242 @@ pub fn logAtoms(self: *MachO) void {...@@ -4145,16 +5261,242 @@ pub fn logAtoms(self: *MachO) void {
4145 }5261 }
4146}5262}
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
4149 const atom = self.getAtom(atom_index);5267 const atom = self.getAtom(atom_index);
4150 const sym = atom.getSymbol(self);5268 const sym = self.getSymbol(atom.getSymbolWithLoc());
4151 const sym_name = atom.getName(self);5269 const sym_name = self.getSymbolName(atom.getSymbolWithLoc());
4152 log.debug(" ATOM(%{?d}, '{s}') @ {x} sizeof({x}) in object({?d}) in sect({d})", .{5270 logger.debug(" ATOM({d}, %{d}, '{s}') @ {x} (sizeof({x}), alignof({x})) in object({?}) in sect({d})", .{
4153 atom.getSymbolIndex(),5271 atom_index,
5272 atom.sym_index,
4154 sym_name,5273 sym_name,
4155 sym.n_value,5274 sym.n_value,
4156 atom.size,5275 atom.size,
4157 atom.file,5276 atom.alignment,
4158 sym.n_sect + 1,5277 atom.getFile(),
5278 sym.n_sect,
4159 });5279 });
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 }
4160}5308}
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 @@...@@ -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
13file: fs.File,1file: fs.File,
14fat_offset: u64,2fat_offset: u64,
15name: []const u8,3name: []const u8,
...@@ -87,6 +75,13 @@ const ar_hdr = extern struct {...@@ -87,6 +75,13 @@ const ar_hdr = extern struct {
87 }75 }
88};76};
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
90pub fn deinit(self: *Archive, allocator: Allocator) void {85pub fn deinit(self: *Archive, allocator: Allocator) void {
91 self.file.close();86 self.file.close();
92 for (self.toc.keys()) |*key| {87 for (self.toc.keys()) |*key| {
...@@ -100,21 +95,8 @@ pub fn deinit(self: *Archive, allocator: Allocator) void {...@@ -100,21 +95,8 @@ pub fn deinit(self: *Archive, allocator: Allocator) void {
100}95}
10196
102pub fn parse(self: *Archive, allocator: Allocator, reader: anytype) !void {97pub fn parse(self: *Archive, allocator: Allocator, reader: anytype) !void {
103 const magic = try reader.readBytesNoEof(SARMAG);98 _ = 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
109 self.header = try reader.readStruct(ar_hdr);99 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
118 const name_or_length = try self.header.nameOrLength();100 const name_or_length = try self.header.nameOrLength();
119 var embedded_name = try parseName(allocator, name_or_length, reader);101 var embedded_name = try parseName(allocator, name_or_length, reader);
120 log.debug("parsing archive '{s}' at '{s}'", .{ embedded_name, self.name });102 log.debug("parsing archive '{s}' at '{s}'", .{ embedded_name, self.name });
...@@ -146,7 +128,7 @@ fn parseTableOfContents(self: *Archive, allocator: Allocator, reader: anytype) !...@@ -146,7 +128,7 @@ fn parseTableOfContents(self: *Archive, allocator: Allocator, reader: anytype) !
146 defer allocator.free(symtab);128 defer allocator.free(symtab);
147129
148 reader.readNoEof(symtab) catch {130 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});
150 return error.MalformedArchive;132 return error.MalformedArchive;
151 };133 };
152134
...@@ -155,7 +137,7 @@ fn parseTableOfContents(self: *Archive, allocator: Allocator, reader: anytype) !...@@ -155,7 +137,7 @@ fn parseTableOfContents(self: *Archive, allocator: Allocator, reader: anytype) !
155 defer allocator.free(strtab);137 defer allocator.free(strtab);
156138
157 reader.readNoEof(strtab) catch {139 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});
159 return error.MalformedArchive;141 return error.MalformedArchive;
160 };142 };
161143
...@@ -182,22 +164,12 @@ fn parseTableOfContents(self: *Archive, allocator: Allocator, reader: anytype) !...@@ -182,22 +164,12 @@ fn parseTableOfContents(self: *Archive, allocator: Allocator, reader: anytype) !
182 }164 }
183}165}
184166
185pub fn parseObject(167pub fn parseObject(self: Archive, gpa: Allocator, offset: u32) !Object {
186 self: Archive,
187 gpa: Allocator,
188 cpu_arch: std.Target.Cpu.Arch,
189 offset: u32,
190) !Object {
191 const reader = self.file.reader();168 const reader = self.file.reader();
192 try reader.context.seekTo(self.fat_offset + offset);169 try reader.context.seekTo(self.fat_offset + offset);
193170
194 const object_header = try reader.readStruct(ar_hdr);171 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
201 const name_or_length = try object_header.nameOrLength();173 const name_or_length = try object_header.nameOrLength();
202 const object_name = try parseName(gpa, name_or_length, reader);174 const object_name = try parseName(gpa, name_or_length, reader);
203 defer gpa.free(object_name);175 defer gpa.free(object_name);
...@@ -227,7 +199,19 @@ pub fn parseObject(...@@ -227,7 +199,19 @@ pub fn parseObject(
227 .contents = contents,199 .contents = contents,
228 };200 };
229201
230 try object.parse(gpa, cpu_arch);202 try object.parse(gpa);
231203
232 return object;204 return object;
233}205}
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 @@...@@ -1,42 +1,39 @@
1const Atom = @This();1/// Each Atom always gets a symbol with the fully qualified name.
22/// The symbol can reside in any object file context structure in `symtab` array
3const std = @import("std");3/// (see `Object`), or if the symbol is a synthetic symbol such as a GOT cell or
4const build_options = @import("build_options");4/// a stub trampoline, it can be found in the linkers `locals` arraylist.
5const aarch64 = @import("../../arch/aarch64/bits.zig");5/// If this field is 0 and file is 0, it means the codegen size = 0 and there is no symbol or
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
25/// offset table entry.6/// offset table entry.
26sym_index: u32,7sym_index: u32 = 0,
278
28/// null means symbol defined by Zig source.9/// 0 means an Atom is a synthetic Atom such as a GOT cell defined by the linker.
29file: ?u32,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
31/// Size and alignment of this atom24/// Size and alignment of this atom
32/// Unlike in Elf, we need to store the size of this symbol as part of25/// Unlike in Elf, we need to store the size of this symbol as part of
33/// the atom since macho.nlist_64 lacks this information.26/// 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
36/// Points to the previous and next neighbours33/// Points to the previous and next neighbours
37/// TODO use the same trick as with symbols: reserve index 0 as null atom34/// TODO use the same trick as with symbols: reserve index 0 as null atom
38next_index: ?Index,35next_index: ?Index = null,
39prev_index: ?Index,36prev_index: ?Index = null,
4037
41pub const Index = u32;38pub const Index = u32;
4239
...@@ -45,13 +42,15 @@ pub const Binding = struct {...@@ -45,13 +42,15 @@ pub const Binding = struct {
45 offset: u64,42 offset: u64,
46};43};
4744
48pub const SymbolAtOffset = struct {45/// Returns `null` if the Atom is a synthetic Atom.
49 sym_index: u32,46/// Otherwise, returns an index into an array of Objects.
50 offset: u64,47pub fn getFile(self: Atom) ?u32 {
51};48 if (self.file == 0) return null;
49 return self.file - 1;
50}
5251
53pub fn getSymbolIndex(self: Atom) ?u32 {52pub 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;
55 return self.sym_index;54 return self.sym_index;
56}55}
5756
...@@ -63,10 +62,7 @@ pub fn getSymbol(self: Atom, macho_file: *MachO) macho.nlist_64 {...@@ -63,10 +62,7 @@ pub fn getSymbol(self: Atom, macho_file: *MachO) macho.nlist_64 {
63/// Returns pointer-to-symbol referencing this atom.62/// Returns pointer-to-symbol referencing this atom.
64pub fn getSymbolPtr(self: Atom, macho_file: *MachO) *macho.nlist_64 {63pub fn getSymbolPtr(self: Atom, macho_file: *MachO) *macho.nlist_64 {
65 const sym_index = self.getSymbolIndex().?;64 const sym_index = self.getSymbolIndex().?;
66 return macho_file.getSymbolPtr(.{65 return macho_file.getSymbolPtr(.{ .sym_index = sym_index, .file = self.file });
67 .sym_index = sym_index,
68 .file = self.file,
69 });
70}66}
7167
72pub fn getSymbolWithLoc(self: Atom) SymbolWithLoc {68pub fn getSymbolWithLoc(self: Atom) SymbolWithLoc {
...@@ -77,10 +73,7 @@ pub fn getSymbolWithLoc(self: Atom) SymbolWithLoc {...@@ -77,10 +73,7 @@ pub fn getSymbolWithLoc(self: Atom) SymbolWithLoc {
77/// Returns the name of this atom.73/// Returns the name of this atom.
78pub fn getName(self: Atom, macho_file: *MachO) []const u8 {74pub fn getName(self: Atom, macho_file: *MachO) []const u8 {
79 const sym_index = self.getSymbolIndex().?;75 const sym_index = self.getSymbolIndex().?;
80 return macho_file.getSymbolName(.{76 return macho_file.getSymbolName(.{ .sym_index = sym_index, .file = self.file });
81 .sym_index = sym_index,
82 .file = self.file,
83 });
84}77}
8578
86/// Returns how much room there is to grow in virtual address space.79/// 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 {...@@ -112,6 +105,147 @@ pub fn freeListEligible(self: Atom, macho_file: *MachO) bool {
112 return surplus >= MachO.min_text_capacity;105 return surplus >= MachO.min_text_capacity;
113}106}
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
115pub fn addRelocation(macho_file: *MachO, atom_index: Index, reloc: Relocation) !void {249pub fn addRelocation(macho_file: *MachO, atom_index: Index, reloc: Relocation) !void {
116 return addRelocations(macho_file, atom_index, &[_]Relocation{reloc});250 return addRelocations(macho_file, atom_index, &[_]Relocation{reloc});
117}251}
...@@ -179,3 +313,946 @@ pub fn freeRelocations(macho_file: *MachO, atom_index: Index) void {...@@ -179,3 +313,946 @@ pub fn freeRelocations(macho_file: *MachO, atom_index: Index) void {
179 var removed_bindings = macho_file.bindings.fetchOrderedRemove(atom_index);313 var removed_bindings = macho_file.bindings.fetchOrderedRemove(atom_index);
180 if (removed_bindings) |*bindings| bindings.value.deinit(gpa);314 if (removed_bindings) |*bindings| bindings.value.deinit(gpa);
181}315}
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 @@...@@ -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");7pub fn init(page_size: u16) CodeSignature {
4const assert = std.debug.assert;8 return .{
5const fs = std.fs;9 .page_size = page_size,
6const log = std.log.scoped(.link);10 .code_directory = CodeDirectory.init(page_size),
7const macho = std.macho;11 };
8const mem = std.mem;12}
9const testing = std.testing;
1013
11const Allocator = mem.Allocator;14pub fn deinit(self: *CodeSignature, allocator: Allocator) void {
12const Compilation = @import("../../Compilation.zig");15 self.code_directory.deinit(allocator);
13const Hasher = @import("hasher.zig").ParallelHasher;16 if (self.requirements) |*req| {
14const Sha256 = std.crypto.hash.sha2.Sha256;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
16const hash_size = Sha256.digest_length;174const hash_size = Sha256.digest_length;
17175
...@@ -218,175 +376,17 @@ const Signature = struct {...@@ -218,175 +376,17 @@ const Signature = struct {
218 }376 }
219};377};
220378
221page_size: u16,379const CodeSignature = @This();
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}
367380
368pub fn estimateSize(self: CodeSignature, file_size: u64) u32 {381const std = @import("std");
369 var ssize: u64 = @sizeOf(macho.SuperBlob) + @sizeOf(macho.BlobIndex) + self.code_directory.size();382const assert = std.debug.assert;
370 // Approx code slots383const fs = std.fs;
371 const total_pages = mem.alignForward(u64, file_size, self.page_size) / self.page_size;384const log = std.log.scoped(.link);
372 ssize += total_pages * hash_size;385const macho = std.macho;
373 var n_special_slots: u32 = 0;386const mem = std.mem;
374 if (self.requirements) |req| {387const testing = std.testing;
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}
388388
389pub fn clear(self: *CodeSignature, allocator: Allocator) void {389const Allocator = mem.Allocator;
390 self.code_directory.deinit(allocator);390const Compilation = @import("../../Compilation.zig");
391 self.code_directory = CodeDirectory.init(self.page_size);391const Hasher = @import("hasher.zig").ParallelHasher;
392}392const Sha256 = std.crypto.hash.sha2.Sha256;
src/link/MachO/DebugSymbols.zig+34-32
...@@ -1,30 +1,6 @@...@@ -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
24allocator: Allocator,1allocator: Allocator,
25dwarf: Dwarf,2dwarf: Dwarf,
26file: fs.File,3file: fs.File,
27page_size: u16,
284
29symtab_cmd: macho.symtab_command = .{},5symtab_cmd: macho.symtab_command = .{},
306
...@@ -62,13 +38,14 @@ pub const Reloc = struct {...@@ -62,13 +38,14 @@ pub const Reloc = struct {
6238
63/// You must call this function *after* `MachO.populateMissingMetadata()`39/// You must call this function *after* `MachO.populateMissingMetadata()`
64/// has been called to get a viable debug symbols output.40/// 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 {
66 if (self.dwarf_segment_cmd_index == null) {42 if (self.dwarf_segment_cmd_index == null) {
67 self.dwarf_segment_cmd_index = @as(u8, @intCast(self.segments.items.len));43 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));
70 const ideal_size: u16 = 200 + 128 + 160 + 250;47 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
73 log.debug("found __DWARF segment free space 0x{x} to 0x{x}", .{ off, off + needed_size });50 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 {...@@ -355,7 +332,8 @@ fn finalizeDwarfSegment(self: *DebugSymbols, macho_file: *MachO) void {
355 file_size = @max(file_size, header.offset + header.size);332 file_size = @max(file_size, header.offset + header.size);
356 }333 }
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);
359 dwarf_segment.vmaddr = base_vmaddr;337 dwarf_segment.vmaddr = base_vmaddr;
360 dwarf_segment.filesize = aligned_size;338 dwarf_segment.filesize = aligned_size;
361 dwarf_segment.vmsize = aligned_size;339 dwarf_segment.vmsize = aligned_size;
...@@ -364,12 +342,12 @@ fn finalizeDwarfSegment(self: *DebugSymbols, macho_file: *MachO) void {...@@ -364,12 +342,12 @@ fn finalizeDwarfSegment(self: *DebugSymbols, macho_file: *MachO) void {
364 linkedit.vmaddr = mem.alignForward(342 linkedit.vmaddr = mem.alignForward(
365 u64,343 u64,
366 dwarf_segment.vmaddr + aligned_size,344 dwarf_segment.vmaddr + aligned_size,
367 self.page_size,345 page_size,
368 );346 );
369 linkedit.fileoff = mem.alignForward(347 linkedit.fileoff = mem.alignForward(
370 u64,348 u64,
371 dwarf_segment.fileoff + aligned_size,349 dwarf_segment.fileoff + aligned_size,
372 self.page_size,350 page_size,
373 );351 );
374 log.debug("found __LINKEDIT segment free space at 0x{x}", .{linkedit.fileoff});352 log.debug("found __LINKEDIT segment free space at 0x{x}", .{linkedit.fileoff});
375}353}
...@@ -457,8 +435,9 @@ fn writeLinkeditSegmentData(self: *DebugSymbols, macho_file: *MachO) !void {...@@ -457,8 +435,9 @@ fn writeLinkeditSegmentData(self: *DebugSymbols, macho_file: *MachO) !void {
457 try self.writeSymtab(macho_file);435 try self.writeSymtab(macho_file);
458 try self.writeStrtab();436 try self.writeStrtab();
459437
438 const page_size = MachO.getPageSize(macho_file.base.options.target.cpu.arch);
460 const seg = &self.segments.items[self.linkedit_segment_cmd_index.?];439 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);
462 seg.vmsize = aligned_size;441 seg.vmsize = aligned_size;
463}442}
464443
...@@ -473,7 +452,7 @@ fn writeSymtab(self: *DebugSymbols, macho_file: *MachO) !void {...@@ -473,7 +452,7 @@ fn writeSymtab(self: *DebugSymbols, macho_file: *MachO) !void {
473452
474 for (macho_file.locals.items, 0..) |sym, sym_id| {453 for (macho_file.locals.items, 0..) |sym, sym_id| {
475 if (sym.n_strx == 0) continue; // no name, skip454 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)) };
477 if (macho_file.symbolIsTemp(sym_loc)) continue; // local temp symbol, skip456 if (macho_file.symbolIsTemp(sym_loc)) continue; // local temp symbol, skip
478 if (macho_file.getGlobal(macho_file.getSymbolName(sym_loc)) != null) continue; // global symbol is either an export or import, skip457 if (macho_file.getGlobal(macho_file.getSymbolName(sym_loc)) != null) continue; // global symbol is either an export or import, skip
479 var out_sym = sym;458 var out_sym = sym;
...@@ -567,3 +546,26 @@ pub fn getSection(self: DebugSymbols, sect: u8) macho.section_64 {...@@ -567,3 +546,26 @@ pub fn getSection(self: DebugSymbols, sect: u8) macho.section_64 {
567 assert(sect < self.sections.items.len);546 assert(sect < self.sections.items.len);
568 return self.sections.items[sect];547 return self.sections.items[sect];
569}548}
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 @@...@@ -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
15debug_info: []const u8,1debug_info: []const u8,
16debug_abbrev: []const u8,2debug_abbrev: []const u8,
17debug_str: []const u8,3debug_str: []const u8,
...@@ -458,7 +444,8 @@ fn findFormSize(self: DwarfInfo, form: u64, di_off: usize, cuh: CompileUnit.Head...@@ -458,7 +444,8 @@ fn findFormSize(self: DwarfInfo, form: u64, di_off: usize, cuh: CompileUnit.Head
458 },444 },
459445
460 else => {446 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});
462 return error.UnhandledDwFormValue;449 return error.UnhandledDwFormValue;
463 },450 },
464 }451 }
...@@ -501,3 +488,17 @@ fn getString(self: DwarfInfo, off: u64) []const u8 {...@@ -501,3 +488,17 @@ fn getString(self: DwarfInfo, off: u64) []const u8 {
501 assert(off < self.debug_str.len);488 assert(off < self.debug_str.len);
502 return mem.sliceTo(@as([*:0]const u8, @ptrCast(self.debug_str.ptr + @as(usize, @intCast(off)))), 0);489 return mem.sliceTo(@as([*:0]const u8, @ptrCast(self.debug_str.ptr + @as(usize, @intCast(off)))), 0);
503}490}
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 @@...@@ -1,25 +1,8 @@
1const Dylib = @This();1path: []const u8,
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
21id: ?Id = null,2id: ?Id = null,
22weak: bool = false,3weak: 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
24/// Parsed symbol table represented as hash map of symbols'7/// Parsed symbol table represented as hash map of symbols'
25/// names. We can and should defer creating *Symbols until8/// names. We can and should defer creating *Symbols until
...@@ -116,7 +99,15 @@ pub const Id = struct {...@@ -116,7 +99,15 @@ pub const Id = struct {
116 }99 }
117};100};
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
119pub fn deinit(self: *Dylib, allocator: Allocator) void {109pub fn deinit(self: *Dylib, allocator: Allocator) void {
110 allocator.free(self.path);
120 for (self.symbols.keys()) |key| {111 for (self.symbols.keys()) |key| {
121 allocator.free(key);112 allocator.free(key);
122 }113 }
...@@ -129,7 +120,6 @@ pub fn deinit(self: *Dylib, allocator: Allocator) void {...@@ -129,7 +120,6 @@ pub fn deinit(self: *Dylib, allocator: Allocator) void {
129pub fn parseFromBinary(120pub fn parseFromBinary(
130 self: *Dylib,121 self: *Dylib,
131 allocator: Allocator,122 allocator: Allocator,
132 cpu_arch: std.Target.Cpu.Arch,
133 dylib_id: u16,123 dylib_id: u16,
134 dependent_libs: anytype,124 dependent_libs: anytype,
135 name: []const u8,125 name: []const u8,
...@@ -140,27 +130,12 @@ pub fn parseFromBinary(...@@ -140,27 +130,12 @@ pub fn parseFromBinary(
140130
141 log.debug("parsing shared library '{s}'", .{name});131 log.debug("parsing shared library '{s}'", .{name});
142132
143 const header = try reader.readStruct(macho.mach_header_64);133 self.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 }
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;
161 var it = LoadCommandIterator{136 var it = LoadCommandIterator{
162 .ncmds = header.ncmds,137 .ncmds = self.header.?.ncmds,
163 .buffer = data[@sizeOf(macho.mach_header_64)..][0..header.sizeofcmds],138 .buffer = data[@sizeOf(macho.mach_header_64)..][0..self.header.?.sizeofcmds],
164 };139 };
165 while (it.next()) |cmd| {140 while (it.next()) |cmd| {
166 switch (cmd.cmd()) {141 switch (cmd.cmd()) {
...@@ -205,6 +180,26 @@ pub fn parseFromBinary(...@@ -205,6 +180,26 @@ pub fn parseFromBinary(
205 }180 }
206}181}
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
208fn addObjCClassSymbol(self: *Dylib, allocator: Allocator, sym_name: []const u8) !void {203fn addObjCClassSymbol(self: *Dylib, allocator: Allocator, sym_name: []const u8) !void {
209 const expanded = &[_][]const u8{204 const expanded = &[_][]const u8{
210 try std.fmt.allocPrint(allocator, "_OBJC_CLASS_$_{s}", .{sym_name}),205 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...@@ -239,41 +234,41 @@ fn addWeakSymbol(self: *Dylib, allocator: Allocator, sym_name: []const u8) !void
239 try self.symbols.putNoClobber(allocator, try allocator.dupe(u8, sym_name), true);234 try self.symbols.putNoClobber(allocator, try allocator.dupe(u8, sym_name), true);
240}235}
241236
242const TargetMatcher = struct {237pub const TargetMatcher = struct {
243 allocator: Allocator,238 allocator: Allocator,
244 target: CrossTarget,239 cpu_arch: std.Target.Cpu.Arch,
240 os_tag: std.Target.Os.Tag,
241 abi: std.Target.Abi,
245 target_strings: std.ArrayListUnmanaged([]const u8) = .{},242 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 {
248 var self = TargetMatcher{245 var self = TargetMatcher{
249 .allocator = allocator,246 .allocator = allocator,
250 .target = target,247 .cpu_arch = target.cpu.arch,
248 .os_tag = target.os.tag,
249 .abi = target.abi,
251 };250 };
252 const apple_string = try targetToAppleString(allocator, target);251 const apple_string = try toAppleTargetTriple(allocator, self.cpu_arch, self.os_tag, self.abi);
253 try self.target_strings.append(allocator, apple_string);252 try self.target_strings.append(allocator, apple_string);
254253
255 const abi = target.abi orelse .none;254 if (self.abi == .simulator) {
256 if (abi == .simulator) {
257 // For Apple simulator targets, linking gets tricky as we need to link against the simulator255 // For Apple simulator targets, linking gets tricky as we need to link against the simulator
258 // hosts dylibs too.256 // hosts dylibs too.
259 const host_target = try targetToAppleString(allocator, .{257 const host_target = try toAppleTargetTriple(allocator, self.cpu_arch, .macos, .none);
260 .cpu_arch = target.cpu_arch.?,
261 .os_tag = .macos,
262 });
263 try self.target_strings.append(allocator, host_target);258 try self.target_strings.append(allocator, host_target);
264 }259 }
265260
266 return self;261 return self;
267 }262 }
268263
269 fn deinit(self: *TargetMatcher) void {264 pub fn deinit(self: *TargetMatcher) void {
270 for (self.target_strings.items) |t| {265 for (self.target_strings.items) |t| {
271 self.allocator.free(t);266 self.allocator.free(t);
272 }267 }
273 self.target_strings.deinit(self.allocator);268 self.target_strings.deinit(self.allocator);
274 }269 }
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 {
277 return switch (cpu_arch) {272 return switch (cpu_arch) {
278 .aarch64 => "arm64",273 .aarch64 => "arm64",
279 .x86_64 => "x86_64",274 .x86_64 => "x86_64",
...@@ -281,7 +276,7 @@ const TargetMatcher = struct {...@@ -281,7 +276,7 @@ const TargetMatcher = struct {
281 };276 };
282 }277 }
283278
284 inline fn abiToAppleString(abi: std.Target.Abi) ?[]const u8 {279 inline fn fmtAbi(abi: std.Target.Abi) ?[]const u8 {
285 return switch (abi) {280 return switch (abi) {
286 .none => null,281 .none => null,
287 .simulator => "simulator",282 .simulator => "simulator",
...@@ -290,14 +285,18 @@ const TargetMatcher = struct {...@@ -290,14 +285,18 @@ const TargetMatcher = struct {
290 };285 };
291 }286 }
292287
293 fn targetToAppleString(allocator: Allocator, target: CrossTarget) ![]const u8 {288 pub fn toAppleTargetTriple(
294 const cpu_arch = cpuArchToAppleString(target.cpu_arch.?);289 allocator: Allocator,
295 const os_tag = @tagName(target.os_tag.?);290 cpu_arch: std.Target.Cpu.Arch,
296 const target_abi = abiToAppleString(target.abi orelse .none);291 os_tag: std.Target.Os.Tag,
297 if (target_abi) |abi| {292 abi: std.Target.Abi,
298 return std.fmt.allocPrint(allocator, "{s}-{s}-{s}", .{ cpu_arch, os_tag, 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 });
299 }298 }
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 });
301 }300 }
302301
303 fn hasValue(stack: []const []const u8, needle: []const u8) bool {302 fn hasValue(stack: []const []const u8, needle: []const u8) bool {
...@@ -307,7 +306,7 @@ const TargetMatcher = struct {...@@ -307,7 +306,7 @@ const TargetMatcher = struct {
307 return false;306 return false;
308 }307 }
309308
310 fn matchesTarget(self: TargetMatcher, targets: []const []const u8) bool {309 pub fn matchesTarget(self: TargetMatcher, targets: []const []const u8) bool {
311 for (self.target_strings.items) |t| {310 for (self.target_strings.items) |t| {
312 if (hasValue(targets, t)) return true;311 if (hasValue(targets, t)) return true;
313 }312 }
...@@ -315,26 +314,7 @@ const TargetMatcher = struct {...@@ -315,26 +314,7 @@ const TargetMatcher = struct {
315 }314 }
316315
317 fn matchesArch(self: TargetMatcher, archs: []const []const u8) bool {316 fn matchesArch(self: TargetMatcher, archs: []const []const u8) bool {
318 return hasValue(archs, cpuArchToAppleString(self.target.cpu_arch.?));317 return hasValue(archs, fmtCpuArch(self.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);
338 }318 }
339};319};
340320
...@@ -347,7 +327,7 @@ pub fn parseFromStub(...@@ -347,7 +327,7 @@ pub fn parseFromStub(
347 dependent_libs: anytype,327 dependent_libs: anytype,
348 name: []const u8,328 name: []const u8,
349) !void {329) !void {
350 if (lib_stub.inner.len == 0) return error.EmptyStubFile;330 if (lib_stub.inner.len == 0) return error.NotLibStub;
351331
352 log.debug("parsing shared library from stub '{s}'", .{name});332 log.debug("parsing shared library from stub '{s}'", .{name});
353333
...@@ -369,15 +349,16 @@ pub fn parseFromStub(...@@ -369,15 +349,16 @@ pub fn parseFromStub(
369349
370 log.debug(" (install_name '{s}')", .{umbrella_lib.installName()});350 log.debug(" (install_name '{s}')", .{umbrella_lib.installName()});
371351
372 var matcher = try TargetMatcher.init(allocator, .{352 var matcher = try TargetMatcher.init(allocator, target);
373 .cpu_arch = target.cpu.arch,
374 .os_tag = target.os.tag,
375 .abi = target.abi,
376 });
377 defer matcher.deinit();353 defer matcher.deinit();
378354
379 for (lib_stub.inner, 0..) |elem, stub_index| {355 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
382 if (stub_index > 0) {363 if (stub_index > 0) {
383 // TODO I thought that we could switch on presence of `parent-umbrella` map;364 // TODO I thought that we could switch on presence of `parent-umbrella` map;
...@@ -553,3 +534,23 @@ pub fn parseFromStub(...@@ -553,3 +534,23 @@ pub fn parseFromStub(
553 }534 }
554 }535 }
555}536}
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 @@...@@ -2,31 +2,6 @@
2//! Each Object is fully loaded into memory for easier2//! Each Object is fully loaded into memory for easier
3//! access into different data within.3//! 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
30name: []const u8,5name: []const u8,
31mtime: u64,6mtime: u64,
32contents: []align(@alignOf(u64)) const u8,7contents: []align(@alignOf(u64)) const u8,
...@@ -54,7 +29,7 @@ source_section_index_lookup: []Entry = undefined,...@@ -54,7 +29,7 @@ source_section_index_lookup: []Entry = undefined,
54/// Can be undefined as set together with in_symtab.29/// Can be undefined as set together with in_symtab.
55strtab_lookup: []u32 = undefined,30strtab_lookup: []u32 = undefined,
56/// Can be undefined as set together with in_symtab.31/// Can be undefined as set together with in_symtab.
57atom_by_index_table: []AtomIndex = undefined,32atom_by_index_table: []?Atom.Index = undefined,
58/// Can be undefined as set together with in_symtab.33/// Can be undefined as set together with in_symtab.
59globals_lookup: []i64 = undefined,34globals_lookup: []i64 = undefined,
60/// Can be undefined as set together with in_symtab.35/// Can be undefined as set together with in_symtab.
...@@ -70,8 +45,8 @@ section_relocs_lookup: std.ArrayListUnmanaged(u32) = .{},...@@ -70,8 +45,8 @@ section_relocs_lookup: std.ArrayListUnmanaged(u32) = .{},
70/// Data-in-code records sorted by address.45/// Data-in-code records sorted by address.
71data_in_code: std.ArrayListUnmanaged(macho.data_in_code_entry) = .{},46data_in_code: std.ArrayListUnmanaged(macho.data_in_code_entry) = .{},
7247
73atoms: std.ArrayListUnmanaged(AtomIndex) = .{},48atoms: std.ArrayListUnmanaged(Atom.Index) = .{},
74exec_atoms: std.ArrayListUnmanaged(AtomIndex) = .{},49exec_atoms: std.ArrayListUnmanaged(Atom.Index) = .{},
7550
76eh_frame_sect_id: ?u8 = null,51eh_frame_sect_id: ?u8 = null,
77eh_frame_relocs_lookup: std.AutoArrayHashMapUnmanaged(u32, Record) = .{},52eh_frame_relocs_lookup: std.AutoArrayHashMapUnmanaged(u32, Record) = .{},
...@@ -91,6 +66,13 @@ const Record = struct {...@@ -91,6 +66,13 @@ const Record = struct {
91 reloc: Entry,66 reloc: Entry,
92};67};
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
94pub fn deinit(self: *Object, gpa: Allocator) void {76pub fn deinit(self: *Object, gpa: Allocator) void {
95 self.atoms.deinit(gpa);77 self.atoms.deinit(gpa);
96 self.exec_atoms.deinit(gpa);78 self.exec_atoms.deinit(gpa);
...@@ -118,36 +100,12 @@ pub fn deinit(self: *Object, gpa: Allocator) void {...@@ -118,36 +100,12 @@ pub fn deinit(self: *Object, gpa: Allocator) void {
118 self.data_in_code.deinit(gpa);100 self.data_in_code.deinit(gpa);
119}101}
120102
121pub fn parse(self: *Object, allocator: Allocator, cpu_arch: std.Target.Cpu.Arch) !void {103pub fn parse(self: *Object, allocator: Allocator) !void {
122 var stream = std.io.fixedBufferStream(self.contents);104 var stream = std.io.fixedBufferStream(self.contents);
123 const reader = stream.reader();105 const reader = stream.reader();
124106
125 self.header = try reader.readStruct(macho.mach_header_64);107 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
151 var it = LoadCommandIterator{109 var it = LoadCommandIterator{
152 .ncmds = self.header.ncmds,110 .ncmds = self.header.ncmds,
153 .buffer = self.contents[@sizeOf(macho.mach_header_64)..][0..self.header.sizeofcmds],111 .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)...@@ -172,7 +130,7 @@ pub fn parse(self: *Object, allocator: Allocator, cpu_arch: std.Target.Cpu.Arch)
172 self.reverse_symtab_lookup = try allocator.alloc(u32, self.in_symtab.?.len);130 self.reverse_symtab_lookup = try allocator.alloc(u32, self.in_symtab.?.len);
173 self.strtab_lookup = try allocator.alloc(u32, self.in_symtab.?.len);131 self.strtab_lookup = try allocator.alloc(u32, self.in_symtab.?.len);
174 self.globals_lookup = try allocator.alloc(i64, self.in_symtab.?.len);132 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);
176 self.relocs_lookup = try allocator.alloc(Entry, self.in_symtab.?.len + nsects);134 self.relocs_lookup = try allocator.alloc(Entry, self.in_symtab.?.len + nsects);
177 // This is wasteful but we need to be able to lookup source symbol address after stripping and135 // This is wasteful but we need to be able to lookup source symbol address after stripping and
178 // allocating of sections.136 // allocating of sections.
...@@ -190,7 +148,7 @@ pub fn parse(self: *Object, allocator: Allocator, cpu_arch: std.Target.Cpu.Arch)...@@ -190,7 +148,7 @@ pub fn parse(self: *Object, allocator: Allocator, cpu_arch: std.Target.Cpu.Arch)
190 }148 }
191149
192 @memset(self.globals_lookup, -1);150 @memset(self.globals_lookup, -1);
193 @memset(self.atom_by_index_table, 0);151 @memset(self.atom_by_index_table, null);
194 @memset(self.source_section_index_lookup, .{});152 @memset(self.source_section_index_lookup, .{});
195 @memset(self.relocs_lookup, .{});153 @memset(self.relocs_lookup, .{});
196154
...@@ -331,10 +289,10 @@ fn filterSymbolsBySection(symbols: []macho.nlist_64, n_sect: u8) struct {...@@ -331,10 +289,10 @@ fn filterSymbolsBySection(symbols: []macho.nlist_64, n_sect: u8) struct {
331 }289 }
332 };290 };
333291
334 const index = @import("zld.zig").lsearch(macho.nlist_64, symbols, FirstMatch{292 const index = MachO.lsearch(macho.nlist_64, symbols, FirstMatch{
335 .n_sect = n_sect,293 .n_sect = n_sect,
336 });294 });
337 const len = @import("zld.zig").lsearch(macho.nlist_64, symbols[index..], FirstNonMatch{295 const len = MachO.lsearch(macho.nlist_64, symbols[index..], FirstNonMatch{
338 .n_sect = n_sect,296 .n_sect = n_sect,
339 });297 });
340298
...@@ -353,10 +311,10 @@ fn filterSymbolsByAddress(symbols: []macho.nlist_64, start_addr: u64, end_addr:...@@ -353,10 +311,10 @@ fn filterSymbolsByAddress(symbols: []macho.nlist_64, start_addr: u64, end_addr:
353 }311 }
354 };312 };
355313
356 const index = @import("zld.zig").lsearch(macho.nlist_64, symbols, Predicate{314 const index = MachO.lsearch(macho.nlist_64, symbols, Predicate{
357 .addr = start_addr,315 .addr = start_addr,
358 });316 });
359 const len = @import("zld.zig").lsearch(macho.nlist_64, symbols[index..], Predicate{317 const len = MachO.lsearch(macho.nlist_64, symbols[index..], Predicate{
360 .addr = end_addr,318 .addr = end_addr,
361 });319 });
362320
...@@ -376,25 +334,32 @@ fn sectionLessThanByAddress(ctx: void, lhs: SortedSection, rhs: SortedSection) b...@@ -376,25 +334,32 @@ fn sectionLessThanByAddress(ctx: void, lhs: SortedSection, rhs: SortedSection) b
376 return lhs.header.addr < rhs.header.addr;334 return lhs.header.addr < rhs.header.addr;
377}335}
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 {
380 log.debug("splitting object({d}, {s}) into atoms", .{ object_id, self.name });345 log.debug("splitting object({d}, {s}) into atoms", .{ object_id, self.name });
381346
382 try self.splitRegularSections(zld, object_id);347 try self.splitRegularSections(macho_file, object_id);
383 try self.parseEhFrameSection(zld, object_id);348 try self.parseEhFrameSection(macho_file, object_id);
384 try self.parseUnwindInfo(zld, object_id);349 try self.parseUnwindInfo(macho_file, object_id);
385 try self.parseDataInCode(zld.gpa);350 try self.parseDataInCode(macho_file.base.allocator);
386}351}
387352
388/// Splits input regular sections into Atoms.353/// Splits input regular sections into Atoms.
389/// If the Object was compiled with `MH_SUBSECTIONS_VIA_SYMBOLS`, splits section354/// If the Object was compiled with `MH_SUBSECTIONS_VIA_SYMBOLS`, splits section
390/// into subsections where each subsection then represents an Atom.355/// into subsections where each subsection then represents an Atom.
391pub fn splitRegularSections(self: *Object, zld: *Zld, object_id: u32) !void {356pub fn splitRegularSections(self: *Object, macho_file: *MachO, object_id: u32) !void {
392 const gpa = zld.gpa;357 const gpa = macho_file.base.allocator;
393358
394 const sections = self.getSourceSections();359 const sections = self.getSourceSections();
395 for (sections, 0..) |sect, id| {360 for (sections, 0..) |sect, id| {
396 if (sect.isDebug()) continue;361 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 {
398 log.debug(" unhandled section '{s},{s}'", .{ sect.segName(), sect.sectName() });363 log.debug(" unhandled section '{s},{s}'", .{ sect.segName(), sect.sectName() });
399 continue;364 continue;
400 };365 };
...@@ -414,13 +379,13 @@ pub fn splitRegularSections(self: *Object, zld: *Zld, object_id: u32) !void {...@@ -414,13 +379,13 @@ pub fn splitRegularSections(self: *Object, zld: *Zld, object_id: u32) !void {
414 if (self.in_symtab == null) {379 if (self.in_symtab == null) {
415 for (sections, 0..) |sect, id| {380 for (sections, 0..) |sect, id| {
416 if (sect.isDebug()) continue;381 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;
418 if (sect.size == 0) continue;383 if (sect.size == 0) continue;
419384
420 const sect_id = @as(u8, @intCast(id));385 const sect_id = @as(u8, @intCast(id));
421 const sym_index = self.getSectionAliasSymbolIndex(sect_id);386 const sym_index = self.getSectionAliasSymbolIndex(sect_id);
422 const atom_index = try self.createAtomFromSubsection(387 const atom_index = try self.createAtomFromSubsection(
423 zld,388 macho_file,
424 object_id,389 object_id,
425 sym_index,390 sym_index,
426 sym_index,391 sym_index,
...@@ -429,7 +394,7 @@ pub fn splitRegularSections(self: *Object, zld: *Zld, object_id: u32) !void {...@@ -429,7 +394,7 @@ pub fn splitRegularSections(self: *Object, zld: *Zld, object_id: u32) !void {
429 sect.@"align",394 sect.@"align",
430 out_sect_id,395 out_sect_id,
431 );396 );
432 zld.addAtomToSection(atom_index);397 macho_file.addAtomToSection(atom_index);
433 }398 }
434 return;399 return;
435 }400 }
...@@ -437,7 +402,7 @@ pub fn splitRegularSections(self: *Object, zld: *Zld, object_id: u32) !void {...@@ -437,7 +402,7 @@ pub fn splitRegularSections(self: *Object, zld: *Zld, object_id: u32) !void {
437 // Well, shit, sometimes compilers skip the dysymtab load command altogether, meaning we402 // Well, shit, sometimes compilers skip the dysymtab load command altogether, meaning we
438 // have to infer the start of undef section in the symtab ourselves.403 // have to infer the start of undef section in the symtab ourselves.
439 const iundefsym = blk: {404 const iundefsym = blk: {
440 const dysymtab = self.parseDysymtab() orelse {405 const dysymtab = self.getDysymtab() orelse {
441 var iundefsym: usize = self.in_symtab.?.len;406 var iundefsym: usize = self.in_symtab.?.len;
442 while (iundefsym > 0) : (iundefsym -= 1) {407 while (iundefsym > 0) : (iundefsym -= 1) {
443 const sym = self.symtab[iundefsym - 1];408 const sym = self.symtab[iundefsym - 1];
...@@ -473,17 +438,17 @@ pub fn splitRegularSections(self: *Object, zld: *Zld, object_id: u32) !void {...@@ -473,17 +438,17 @@ pub fn splitRegularSections(self: *Object, zld: *Zld, object_id: u32) !void {
473 log.debug("splitting section '{s},{s}' into atoms", .{ sect.segName(), sect.sectName() });438 log.debug("splitting section '{s},{s}' into atoms", .{ sect.segName(), sect.sectName() });
474439
475 // Get output segment/section in the final artifact.440 // 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
478 log.debug(" output sect({d}, '{s},{s}')", .{443 log.debug(" output sect({d}, '{s},{s}')", .{
479 out_sect_id + 1,444 out_sect_id + 1,
480 zld.sections.items(.header)[out_sect_id].segName(),445 macho_file.sections.items(.header)[out_sect_id].segName(),
481 zld.sections.items(.header)[out_sect_id].sectName(),446 macho_file.sections.items(.header)[out_sect_id].sectName(),
482 });447 });
483448
484 try self.parseRelocs(gpa, section.id);449 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;
487 const sect_loc = filterSymbolsBySection(symtab[sect_sym_index..], sect_id + 1);452 const sect_loc = filterSymbolsBySection(symtab[sect_sym_index..], sect_id + 1);
488 const sect_start_index = sect_sym_index + sect_loc.index;453 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 {...@@ -499,7 +464,7 @@ pub fn splitRegularSections(self: *Object, zld: *Zld, object_id: u32) !void {
499 const sym_index = self.getSectionAliasSymbolIndex(sect_id);464 const sym_index = self.getSectionAliasSymbolIndex(sect_id);
500 const atom_size = first_sym.n_value - sect.addr;465 const atom_size = first_sym.n_value - sect.addr;
501 const atom_index = try self.createAtomFromSubsection(466 const atom_index = try self.createAtomFromSubsection(
502 zld,467 macho_file,
503 object_id,468 object_id,
504 sym_index,469 sym_index,
505 sym_index,470 sym_index,
...@@ -509,9 +474,9 @@ pub fn splitRegularSections(self: *Object, zld: *Zld, object_id: u32) !void {...@@ -509,9 +474,9 @@ pub fn splitRegularSections(self: *Object, zld: *Zld, object_id: u32) !void {
509 out_sect_id,474 out_sect_id,
510 );475 );
511 if (!sect.isZerofill()) {476 if (!sect.isZerofill()) {
512 try self.cacheRelocs(zld, atom_index);477 try self.cacheRelocs(macho_file, atom_index);
513 }478 }
514 zld.addAtomToSection(atom_index);479 macho_file.addAtomToSection(atom_index);
515 }480 }
516481
517 var next_sym_index = sect_start_index;482 var next_sym_index = sect_start_index;
...@@ -535,7 +500,7 @@ pub fn splitRegularSections(self: *Object, zld: *Zld, object_id: u32) !void {...@@ -535,7 +500,7 @@ pub fn splitRegularSections(self: *Object, zld: *Zld, object_id: u32) !void {
535 sect.@"align";500 sect.@"align";
536501
537 const atom_index = try self.createAtomFromSubsection(502 const atom_index = try self.createAtomFromSubsection(
538 zld,503 macho_file,
539 object_id,504 object_id,
540 atom_sym_index,505 atom_sym_index,
541 atom_sym_index,506 atom_sym_index,
...@@ -554,14 +519,14 @@ pub fn splitRegularSections(self: *Object, zld: *Zld, object_id: u32) !void {...@@ -554,14 +519,14 @@ pub fn splitRegularSections(self: *Object, zld: *Zld, object_id: u32) !void {
554 self.atom_by_index_table[alias_index] = atom_index;519 self.atom_by_index_table[alias_index] = atom_index;
555 }520 }
556 if (!sect.isZerofill()) {521 if (!sect.isZerofill()) {
557 try self.cacheRelocs(zld, atom_index);522 try self.cacheRelocs(macho_file, atom_index);
558 }523 }
559 zld.addAtomToSection(atom_index);524 macho_file.addAtomToSection(atom_index);
560 }525 }
561 } else {526 } else {
562 const alias_index = self.getSectionAliasSymbolIndex(sect_id);527 const alias_index = self.getSectionAliasSymbolIndex(sect_id);
563 const atom_index = try self.createAtomFromSubsection(528 const atom_index = try self.createAtomFromSubsection(
564 zld,529 macho_file,
565 object_id,530 object_id,
566 alias_index,531 alias_index,
567 sect_start_index,532 sect_start_index,
...@@ -571,16 +536,16 @@ pub fn splitRegularSections(self: *Object, zld: *Zld, object_id: u32) !void {...@@ -571,16 +536,16 @@ pub fn splitRegularSections(self: *Object, zld: *Zld, object_id: u32) !void {
571 out_sect_id,536 out_sect_id,
572 );537 );
573 if (!sect.isZerofill()) {538 if (!sect.isZerofill()) {
574 try self.cacheRelocs(zld, atom_index);539 try self.cacheRelocs(macho_file, atom_index);
575 }540 }
576 zld.addAtomToSection(atom_index);541 macho_file.addAtomToSection(atom_index);
577 }542 }
578 }543 }
579}544}
580545
581fn createAtomFromSubsection(546fn createAtomFromSubsection(
582 self: *Object,547 self: *Object,
583 zld: *Zld,548 macho_file: *MachO,
584 object_id: u32,549 object_id: u32,
585 sym_index: u32,550 sym_index: u32,
586 inner_sym_index: u32,551 inner_sym_index: u32,
...@@ -588,10 +553,10 @@ fn createAtomFromSubsection(...@@ -588,10 +553,10 @@ fn createAtomFromSubsection(
588 size: u64,553 size: u64,
589 alignment: u32,554 alignment: u32,
590 out_sect_id: u8,555 out_sect_id: u8,
591) !AtomIndex {556) !Atom.Index {
592 const gpa = zld.gpa;557 const gpa = macho_file.base.allocator;
593 const atom_index = try zld.createEmptyAtom(sym_index, size, alignment);558 const atom_index = try macho_file.createAtom(sym_index, .{ .size = size, .alignment = alignment });
594 const atom = zld.getAtomPtr(atom_index);559 const atom = macho_file.getAtomPtr(atom_index);
595 atom.inner_sym_index = inner_sym_index;560 atom.inner_sym_index = inner_sym_index;
596 atom.inner_nsyms_trailing = inner_nsyms_trailing;561 atom.inner_nsyms_trailing = inner_nsyms_trailing;
597 atom.file = object_id + 1;562 atom.file = object_id + 1;
...@@ -601,22 +566,22 @@ fn createAtomFromSubsection(...@@ -601,22 +566,22 @@ fn createAtomFromSubsection(
601 sym_index,566 sym_index,
602 self.getSymbolName(sym_index),567 self.getSymbolName(sym_index),
603 out_sect_id + 1,568 out_sect_id + 1,
604 zld.sections.items(.header)[out_sect_id].segName(),569 macho_file.sections.items(.header)[out_sect_id].segName(),
605 zld.sections.items(.header)[out_sect_id].sectName(),570 macho_file.sections.items(.header)[out_sect_id].sectName(),
606 object_id,571 object_id,
607 });572 });
608573
609 try self.atoms.append(gpa, atom_index);574 try self.atoms.append(gpa, atom_index);
610 self.atom_by_index_table[sym_index] = atom_index;575 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);
613 while (it.next()) |sym_loc| {578 while (it.next()) |sym_loc| {
614 const inner = zld.getSymbolPtr(sym_loc);579 const inner = macho_file.getSymbolPtr(sym_loc);
615 inner.n_sect = out_sect_id + 1;580 inner.n_sect = out_sect_id + 1;
616 self.atom_by_index_table[sym_loc.sym_index] = atom_index;581 self.atom_by_index_table[sym_loc.sym_index] = atom_index;
617 }582 }
618583
619 const out_sect = zld.sections.items(.header)[out_sect_id];584 const out_sect = macho_file.sections.items(.header)[out_sect_id];
620 if (out_sect.isCode() and585 if (out_sect.isCode() and
621 mem.eql(u8, "__TEXT", out_sect.segName()) and586 mem.eql(u8, "__TEXT", out_sect.segName()) and
622 mem.eql(u8, "__text", out_sect.sectName()))587 mem.eql(u8, "__text", out_sect.sectName()))
...@@ -648,8 +613,8 @@ fn filterRelocs(...@@ -648,8 +613,8 @@ fn filterRelocs(
648 }613 }
649 };614 };
650615
651 const start = @import("zld.zig").bsearch(macho.relocation_info, relocs, Predicate{ .addr = end_addr });616 const start = MachO.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 });617 const len = MachO.lsearch(macho.relocation_info, relocs[start..], LPredicate{ .addr = start_addr });
653618
654 return .{ .start = @as(u32, @intCast(start)), .len = @as(u32, @intCast(len)) };619 return .{ .start = @as(u32, @intCast(start)), .len = @as(u32, @intCast(len)) };
655}620}
...@@ -668,8 +633,8 @@ fn parseRelocs(self: *Object, gpa: Allocator, sect_id: u8) !void {...@@ -668,8 +633,8 @@ fn parseRelocs(self: *Object, gpa: Allocator, sect_id: u8) !void {
668 self.section_relocs_lookup.items[sect_id] = start;633 self.section_relocs_lookup.items[sect_id] = start;
669}634}
670635
671fn cacheRelocs(self: *Object, zld: *Zld, atom_index: AtomIndex) !void {636fn cacheRelocs(self: *Object, macho_file: *MachO, atom_index: Atom.Index) !void {
672 const atom = zld.getAtom(atom_index);637 const atom = macho_file.getAtom(atom_index);
673638
674 const source_sect_id = if (self.getSourceSymbol(atom.sym_index)) |source_sym| blk: {639 const source_sect_id = if (self.getSourceSymbol(atom.sym_index)) |source_sym| blk: {
675 break :blk source_sym.n_sect - 1;640 break :blk source_sym.n_sect - 1;
...@@ -696,18 +661,19 @@ fn relocGreaterThan(ctx: void, lhs: macho.relocation_info, rhs: macho.relocation...@@ -696,18 +661,19 @@ fn relocGreaterThan(ctx: void, lhs: macho.relocation_info, rhs: macho.relocation
696 return lhs.r_address > rhs.r_address;661 return lhs.r_address > rhs.r_address;
697}662}
698663
699fn parseEhFrameSection(self: *Object, zld: *Zld, object_id: u32) !void {664fn parseEhFrameSection(self: *Object, macho_file: *MachO, object_id: u32) !void {
700 const sect_id = self.eh_frame_sect_id orelse return;665 const sect_id = self.eh_frame_sect_id orelse return;
701 const sect = self.getSourceSection(sect_id);666 const sect = self.getSourceSection(sect_id);
702667
703 log.debug("parsing __TEXT,__eh_frame section", .{});668 log.debug("parsing __TEXT,__eh_frame section", .{});
704669
705 if (zld.getSectionByName("__TEXT", "__eh_frame") == null) {670 const gpa = macho_file.base.allocator;
706 _ = try zld.initSection("__TEXT", "__eh_frame", .{});671
672 if (macho_file.eh_frame_section_index == null) {
673 macho_file.eh_frame_section_index = try macho_file.initSection("__TEXT", "__eh_frame", .{});
707 }674 }
708675
709 const gpa = zld.gpa;676 const cpu_arch = macho_file.base.options.target.cpu.arch;
710 const cpu_arch = zld.options.target.cpu.arch;
711 try self.parseRelocs(gpa, sect_id);677 try self.parseRelocs(gpa, sect_id);
712 const relocs = self.getRelocs(sect_id);678 const relocs = self.getRelocs(sect_id);
713679
...@@ -745,7 +711,7 @@ fn parseEhFrameSection(self: *Object, zld: *Zld, object_id: u32) !void {...@@ -745,7 +711,7 @@ fn parseEhFrameSection(self: *Object, zld: *Zld, object_id: u32) !void {
745 @as(macho.reloc_type_arm64, @enumFromInt(rel.r_type)) == .ARM64_RELOC_UNSIGNED)711 @as(macho.reloc_type_arm64, @enumFromInt(rel.r_type)) == .ARM64_RELOC_UNSIGNED)
746 break rel;712 break rel;
747 } else unreachable;713 } else unreachable;
748 const target = Atom.parseRelocTarget(zld, .{714 const target = Atom.parseRelocTarget(macho_file, .{
749 .object_id = object_id,715 .object_id = object_id,
750 .rel = rel,716 .rel = rel,
751 .code = it.data[offset..],717 .code = it.data[offset..],
...@@ -760,7 +726,7 @@ fn parseEhFrameSection(self: *Object, zld: *Zld, object_id: u32) !void {...@@ -760,7 +726,7 @@ fn parseEhFrameSection(self: *Object, zld: *Zld, object_id: u32) !void {
760 });726 });
761 const target_sym_index = self.getSymbolByAddress(target_address, null);727 const target_sym_index = self.getSymbolByAddress(target_address, null);
762 const target = if (self.getGlobal(target_sym_index)) |global_index|728 const target = if (self.getGlobal(target_sym_index)) |global_index|
763 zld.globals.items[global_index]729 macho_file.globals.items[global_index]
764 else730 else
765 SymbolWithLoc{ .sym_index = target_sym_index, .file = object_id + 1 };731 SymbolWithLoc{ .sym_index = target_sym_index, .file = object_id + 1 };
766 break :blk target;732 break :blk target;
...@@ -786,7 +752,7 @@ fn parseEhFrameSection(self: *Object, zld: *Zld, object_id: u32) !void {...@@ -786,7 +752,7 @@ fn parseEhFrameSection(self: *Object, zld: *Zld, object_id: u32) !void {
786 };752 };
787 log.debug("FDE at offset {x} tracks {s}", .{753 log.debug("FDE at offset {x} tracks {s}", .{
788 offset,754 offset,
789 zld.getSymbolName(actual_target),755 macho_file.getSymbolName(actual_target),
790 });756 });
791 try self.eh_frame_records_lookup.putNoClobber(gpa, actual_target, offset);757 try self.eh_frame_records_lookup.putNoClobber(gpa, actual_target, offset);
792 }758 }
...@@ -795,15 +761,21 @@ fn parseEhFrameSection(self: *Object, zld: *Zld, object_id: u32) !void {...@@ -795,15 +761,21 @@ fn parseEhFrameSection(self: *Object, zld: *Zld, object_id: u32) !void {
795 }761 }
796}762}
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;
799 const sect_id = self.unwind_info_sect_id orelse {767 const sect_id = self.unwind_info_sect_id orelse {
800 // If it so happens that the object had `__eh_frame` section defined but no `__compact_unwind`,768 // If it so happens that the object had `__eh_frame` section defined but no `__compact_unwind`,
801 // we will try fully synthesising unwind info records to somewhat match Apple ld's769 // we will try fully synthesising unwind info records to somewhat match Apple ld's
802 // approach. However, we will only synthesise DWARF records and nothing more. For this reason,770 // approach. However, we will only synthesise DWARF records and nothing more. For this reason,
803 // we still create the output `__TEXT,__unwind_info` section.771 // we still create the output `__TEXT,__unwind_info` section.
804 if (self.hasEhFrameRecords()) {772 if (self.hasEhFrameRecords()) {
805 if (zld.getSectionByName("__TEXT", "__unwind_info") == null) {773 if (macho_file.unwind_info_section_index == null) {
806 _ = try zld.initSection("__TEXT", "__unwind_info", .{});774 macho_file.unwind_info_section_index = try macho_file.initSection(
775 "__TEXT",
776 "__unwind_info",
777 .{},
778 );
807 }779 }
808 }780 }
809 return;781 return;
...@@ -811,11 +783,8 @@ fn parseUnwindInfo(self: *Object, zld: *Zld, object_id: u32) !void {...@@ -811,11 +783,8 @@ fn parseUnwindInfo(self: *Object, zld: *Zld, object_id: u32) !void {
811783
812 log.debug("parsing unwind info in {s}", .{self.name});784 log.debug("parsing unwind info in {s}", .{self.name});
813785
814 const gpa = zld.gpa;786 if (macho_file.unwind_info_section_index == null) {
815 const cpu_arch = zld.options.target.cpu.arch;787 macho_file.unwind_info_section_index = try macho_file.initSection("__TEXT", "__unwind_info", .{});
816
817 if (zld.getSectionByName("__TEXT", "__unwind_info") == null) {
818 _ = try zld.initSection("__TEXT", "__unwind_info", .{});
819 }788 }
820789
821 const unwind_records = self.getUnwindRecords();790 const unwind_records = self.getUnwindRecords();
...@@ -826,11 +795,7 @@ fn parseUnwindInfo(self: *Object, zld: *Zld, object_id: u32) !void {...@@ -826,11 +795,7 @@ fn parseUnwindInfo(self: *Object, zld: *Zld, object_id: u32) !void {
826 if (UnwindInfo.UnwindEncoding.isDwarf(record.compactUnwindEncoding, cpu_arch)) break true;795 if (UnwindInfo.UnwindEncoding.isDwarf(record.compactUnwindEncoding, cpu_arch)) break true;
827 } else false;796 } else false;
828797
829 if (needs_eh_frame and !self.hasEhFrameRecords()) {798 if (needs_eh_frame and !self.hasEhFrameRecords()) return error.MissingEhFrameSection;
830 log.err("missing __TEXT,__eh_frame section", .{});
831 log.err(" in object {s}", .{self.name});
832 return error.MissingSection;
833 }
834799
835 try self.parseRelocs(gpa, sect_id);800 try self.parseRelocs(gpa, sect_id);
836 const relocs = self.getRelocs(sect_id);801 const relocs = self.getRelocs(sect_id);
...@@ -850,7 +815,7 @@ fn parseUnwindInfo(self: *Object, zld: *Zld, object_id: u32) !void {...@@ -850,7 +815,7 @@ fn parseUnwindInfo(self: *Object, zld: *Zld, object_id: u32) !void {
850815
851 // Find function symbol that this record describes816 // Find function symbol that this record describes
852 const rel = relocs[rel_pos.start..][rel_pos.len - 1];817 const rel = relocs[rel_pos.start..][rel_pos.len - 1];
853 const target = Atom.parseRelocTarget(zld, .{818 const target = Atom.parseRelocTarget(macho_file, .{
854 .object_id = object_id,819 .object_id = object_id,
855 .rel = rel,820 .rel = rel,
856 .code = mem.asBytes(&record),821 .code = mem.asBytes(&record),
...@@ -874,7 +839,7 @@ fn parseUnwindInfo(self: *Object, zld: *Zld, object_id: u32) !void {...@@ -874,7 +839,7 @@ fn parseUnwindInfo(self: *Object, zld: *Zld, object_id: u32) !void {
874 };839 };
875 log.debug("unwind record {d} tracks {s}", .{840 log.debug("unwind record {d} tracks {s}", .{
876 record_id,841 record_id,
877 zld.getSymbolName(actual_target),842 macho_file.getSymbolName(actual_target),
878 });843 });
879 try self.unwind_records_lookup.putNoClobber(gpa, actual_target, @intCast(record_id));844 try self.unwind_records_lookup.putNoClobber(gpa, actual_target, @intCast(record_id));
880 }845 }
...@@ -945,16 +910,14 @@ fn diceLessThan(ctx: void, lhs: macho.data_in_code_entry, rhs: macho.data_in_cod...@@ -945,16 +910,14 @@ fn diceLessThan(ctx: void, lhs: macho.data_in_code_entry, rhs: macho.data_in_cod
945 return lhs.offset < rhs.offset;910 return lhs.offset < rhs.offset;
946}911}
947912
948fn parseDysymtab(self: Object) ?macho.dysymtab_command {913fn getDysymtab(self: Object) ?macho.dysymtab_command {
949 var it = LoadCommandIterator{914 var it = LoadCommandIterator{
950 .ncmds = self.header.ncmds,915 .ncmds = self.header.ncmds,
951 .buffer = self.contents[@sizeOf(macho.mach_header_64)..][0..self.header.sizeofcmds],916 .buffer = self.contents[@sizeOf(macho.mach_header_64)..][0..self.header.sizeofcmds],
952 };917 };
953 while (it.next()) |cmd| {918 while (it.next()) |cmd| {
954 switch (cmd.cmd()) {919 switch (cmd.cmd()) {
955 .DYSYMTAB => {920 .DYSYMTAB => return cmd.cast(macho.dysymtab_command).?,
956 return cmd.cast(macho.dysymtab_command).?;
957 },
958 else => {},921 else => {},
959 }922 }
960 } else return null;923 } else return null;
...@@ -980,6 +943,26 @@ pub fn parseDwarfInfo(self: Object) DwarfInfo {...@@ -980,6 +943,26 @@ pub fn parseDwarfInfo(self: Object) DwarfInfo {
980 return di;943 return di;
981}944}
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
983pub fn getSectionContents(self: Object, sect: macho.section_64) []const u8 {966pub fn getSectionContents(self: Object, sect: macho.section_64) []const u8 {
984 const size = @as(usize, @intCast(sect.size));967 const size = @as(usize, @intCast(sect.size));
985 return self.contents[sect.offset..][0..size];968 return self.contents[sect.offset..][0..size];
...@@ -1050,7 +1033,7 @@ pub fn getSymbolByAddress(self: Object, addr: u64, sect_hint: ?u8) u32 {...@@ -1050,7 +1033,7 @@ pub fn getSymbolByAddress(self: Object, addr: u64, sect_hint: ?u8) u32 {
1050 if (sect_hint) |sect_id| {1033 if (sect_hint) |sect_id| {
1051 if (self.source_section_index_lookup[sect_id].len > 0) {1034 if (self.source_section_index_lookup[sect_id].len > 0) {
1052 const lookup = self.source_section_index_lookup[sect_id];1035 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(
1054 i64,1037 i64,
1055 self.source_address_lookup[lookup.start..][0..lookup.len],1038 self.source_address_lookup[lookup.start..][0..lookup.len],
1056 Predicate{ .addr = @as(i64, @intCast(addr)) },1039 Predicate{ .addr = @as(i64, @intCast(addr)) },
...@@ -1065,7 +1048,7 @@ pub fn getSymbolByAddress(self: Object, addr: u64, sect_hint: ?u8) u32 {...@@ -1065,7 +1048,7 @@ pub fn getSymbolByAddress(self: Object, addr: u64, sect_hint: ?u8) u32 {
1065 return self.getSectionAliasSymbolIndex(sect_id);1048 return self.getSectionAliasSymbolIndex(sect_id);
1066 }1049 }
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{
1069 .addr = @as(i64, @intCast(addr)),1052 .addr = @as(i64, @intCast(addr)),
1070 });1053 });
1071 assert(target_sym_index > 0);1054 assert(target_sym_index > 0);
...@@ -1077,10 +1060,8 @@ pub fn getGlobal(self: Object, sym_index: u32) ?u32 {...@@ -1077,10 +1060,8 @@ pub fn getGlobal(self: Object, sym_index: u32) ?u32 {
1077 return @as(u32, @intCast(self.globals_lookup[sym_index]));1060 return @as(u32, @intCast(self.globals_lookup[sym_index]));
1078}1061}
10791062
1080pub fn getAtomIndexForSymbol(self: Object, sym_index: u32) ?AtomIndex {1063pub fn getAtomIndexForSymbol(self: Object, sym_index: u32) ?Atom.Index {
1081 const atom_index = self.atom_by_index_table[sym_index];1064 return self.atom_by_index_table[sym_index];
1082 if (atom_index == 0) return null;
1083 return atom_index;
1084}1065}
10851066
1086pub fn hasUnwindRecords(self: Object) bool {1067pub fn hasUnwindRecords(self: Object) bool {
...@@ -1109,3 +1090,28 @@ pub fn getEhFrameRecordsIterator(self: Object) eh_frame.Iterator {...@@ -1109,3 +1090,28 @@ pub fn getEhFrameRecordsIterator(self: Object) eh_frame.Iterator {
1109pub fn hasDataInCode(self: Object) bool {1090pub fn hasDataInCode(self: Object) bool {
1110 return self.data_in_code.items.len > 0;1091 return self.data_in_code.items.len > 0;
1111}1092}
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 {...@@ -62,7 +62,7 @@ pub fn getTargetBaseAddress(self: Relocation, macho_file: *MachO) ?u64 {
62 const index = macho_file.stub_table.lookup.get(self.target) orelse return null;62 const index = macho_file.stub_table.lookup.get(self.target) orelse return null;
63 const header = macho_file.sections.items(.header)[macho_file.stubs_section_index.?];63 const header = macho_file.sections.items(.header)[macho_file.stubs_section_index.?];
64 return header.addr +64 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);
66 }66 }
67 switch (self.type) {67 switch (self.type) {
68 .got, .got_page, .got_pageoff => {68 .got, .got_page, .got_pageoff => {
src/link/MachO/Trie.zig+242-242
...@@ -28,248 +28,6 @@...@@ -28,248 +28,6 @@
28//! After the optional exported symbol information is a byte of how many edges (0-255) that28//! After the optional exported symbol information is a byte of how many edges (0-255) that
29//! this node has leaving it, followed by each edge. Each edge is a zero terminated UTF8 of29//! this node has leaving it, followed by each edge. Each edge is a zero terminated UTF8 of
30//! the addition chars in the symbol, followed by a uleb128 offset for the node that edge points to.30//! 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
273/// The root node of the trie.31/// The root node of the trie.
274root: ?*Node = null,32root: ?*Node = null,
27533
...@@ -611,3 +369,245 @@ test "ordering bug" {...@@ -611,3 +369,245 @@ test "ordering bug" {
611 _ = try trie.write(stream.writer());369 _ = try trie.write(stream.writer());
612 try expectEqualHexStrings(&exp_buffer, buffer);370 try expectEqualHexStrings(&exp_buffer, buffer);
613}371}
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 @@...@@ -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
24gpa: Allocator,1gpa: Allocator,
252
26/// List of all unwind records gathered from all objects and sorted3/// List of all unwind records gathered from all objects and sorted
...@@ -204,28 +181,28 @@ pub fn deinit(info: *UnwindInfo) void {...@@ -204,28 +181,28 @@ pub fn deinit(info: *UnwindInfo) void {
204 info.lsdas_lookup.deinit(info.gpa);181 info.lsdas_lookup.deinit(info.gpa);
205}182}
206183
207pub fn scanRelocs(zld: *Zld) !void {184pub fn scanRelocs(macho_file: *MachO) !void {
208 if (zld.getSectionByName("__TEXT", "__unwind_info") == null) return;185 if (macho_file.unwind_info_section_index == null) return;
209186
210 const cpu_arch = zld.options.target.cpu.arch;187 const cpu_arch = macho_file.base.options.target.cpu.arch;
211 for (zld.objects.items, 0..) |*object, object_id| {188 for (macho_file.objects.items, 0..) |*object, object_id| {
212 const unwind_records = object.getUnwindRecords();189 const unwind_records = object.getUnwindRecords();
213 for (object.exec_atoms.items) |atom_index| {190 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);
215 while (inner_syms_it.next()) |sym| {192 while (inner_syms_it.next()) |sym| {
216 const record_id = object.unwind_records_lookup.get(sym) orelse continue;193 const record_id = object.unwind_records_lookup.get(sym) orelse continue;
217 if (object.unwind_relocs_lookup[record_id].dead) continue;194 if (object.unwind_relocs_lookup[record_id].dead) continue;
218 const record = unwind_records[record_id];195 const record = unwind_records[record_id];
219 if (!UnwindEncoding.isDwarf(record.compactUnwindEncoding, cpu_arch)) {196 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| {
221 // Personality function; add GOT pointer.198 // Personality function; add GOT pointer.
222 const target = Atom.parseRelocTarget(zld, .{199 const target = Atom.parseRelocTarget(macho_file, .{
223 .object_id = @as(u32, @intCast(object_id)),200 .object_id = @as(u32, @intCast(object_id)),
224 .rel = rel,201 .rel = rel,
225 .code = mem.asBytes(&record),202 .code = mem.asBytes(&record),
226 .base_offset = @as(i32, @intCast(record_id * @sizeOf(macho.compact_unwind_entry))),203 .base_offset = @as(i32, @intCast(record_id * @sizeOf(macho.compact_unwind_entry))),
227 });204 });
228 try Atom.addGotEntry(zld, target);205 try macho_file.addGotEntry(target);
229 }206 }
230 }207 }
231 }208 }
...@@ -233,10 +210,10 @@ pub fn scanRelocs(zld: *Zld) !void {...@@ -233,10 +210,10 @@ pub fn scanRelocs(zld: *Zld) !void {
233 }210 }
234}211}
235212
236pub fn collect(info: *UnwindInfo, zld: *Zld) !void {213pub fn collect(info: *UnwindInfo, macho_file: *MachO) !void {
237 if (zld.getSectionByName("__TEXT", "__unwind_info") == null) return;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
241 var records = std.ArrayList(macho.compact_unwind_entry).init(info.gpa);218 var records = std.ArrayList(macho.compact_unwind_entry).init(info.gpa);
242 defer records.deinit();219 defer records.deinit();
...@@ -245,7 +222,7 @@ pub fn collect(info: *UnwindInfo, zld: *Zld) !void {...@@ -245,7 +222,7 @@ pub fn collect(info: *UnwindInfo, zld: *Zld) !void {
245 defer sym_indexes.deinit();222 defer sym_indexes.deinit();
246223
247 // TODO handle dead stripping224 // TODO handle dead stripping
248 for (zld.objects.items, 0..) |*object, object_id| {225 for (macho_file.objects.items, 0..) |*object, object_id| {
249 log.debug("collecting unwind records in {s} ({d})", .{ object.name, object_id });226 log.debug("collecting unwind records in {s} ({d})", .{ object.name, object_id });
250 const unwind_records = object.getUnwindRecords();227 const unwind_records = object.getUnwindRecords();
251228
...@@ -255,7 +232,7 @@ pub fn collect(info: *UnwindInfo, zld: *Zld) !void {...@@ -255,7 +232,7 @@ pub fn collect(info: *UnwindInfo, zld: *Zld) !void {
255 try sym_indexes.ensureUnusedCapacity(object.exec_atoms.items.len);232 try sym_indexes.ensureUnusedCapacity(object.exec_atoms.items.len);
256233
257 for (object.exec_atoms.items) |atom_index| {234 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);
259 var prev_symbol: ?SymbolWithLoc = null;236 var prev_symbol: ?SymbolWithLoc = null;
260 while (inner_syms_it.next()) |symbol| {237 while (inner_syms_it.next()) |symbol| {
261 var record = if (object.unwind_records_lookup.get(symbol)) |record_id| blk: {238 var record = if (object.unwind_records_lookup.get(symbol)) |record_id| blk: {
...@@ -263,14 +240,14 @@ pub fn collect(info: *UnwindInfo, zld: *Zld) !void {...@@ -263,14 +240,14 @@ pub fn collect(info: *UnwindInfo, zld: *Zld) !void {
263 var record = unwind_records[record_id];240 var record = unwind_records[record_id];
264241
265 if (UnwindEncoding.isDwarf(record.compactUnwindEncoding, cpu_arch)) {242 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);
267 } else {244 } else {
268 if (getPersonalityFunctionReloc(245 if (getPersonalityFunctionReloc(
269 zld,246 macho_file,
270 @as(u32, @intCast(object_id)),247 @as(u32, @intCast(object_id)),
271 record_id,248 record_id,
272 )) |rel| {249 )) |rel| {
273 const target = Atom.parseRelocTarget(zld, .{250 const target = Atom.parseRelocTarget(macho_file, .{
274 .object_id = @as(u32, @intCast(object_id)),251 .object_id = @as(u32, @intCast(object_id)),
275 .rel = rel,252 .rel = rel,
276 .code = mem.asBytes(&record),253 .code = mem.asBytes(&record),
...@@ -287,8 +264,8 @@ pub fn collect(info: *UnwindInfo, zld: *Zld) !void {...@@ -287,8 +264,8 @@ pub fn collect(info: *UnwindInfo, zld: *Zld) !void {
287 UnwindEncoding.setPersonalityIndex(&record.compactUnwindEncoding, personality_index + 1);264 UnwindEncoding.setPersonalityIndex(&record.compactUnwindEncoding, personality_index + 1);
288 }265 }
289266
290 if (getLsdaReloc(zld, @as(u32, @intCast(object_id)), record_id)) |rel| {267 if (getLsdaReloc(macho_file, @as(u32, @intCast(object_id)), record_id)) |rel| {
291 const target = Atom.parseRelocTarget(zld, .{268 const target = Atom.parseRelocTarget(macho_file, .{
292 .object_id = @as(u32, @intCast(object_id)),269 .object_id = @as(u32, @intCast(object_id)),
293 .rel = rel,270 .rel = rel,
294 .code = mem.asBytes(&record),271 .code = mem.asBytes(&record),
...@@ -299,8 +276,8 @@ pub fn collect(info: *UnwindInfo, zld: *Zld) !void {...@@ -299,8 +276,8 @@ pub fn collect(info: *UnwindInfo, zld: *Zld) !void {
299 }276 }
300 break :blk record;277 break :blk record;
301 } else blk: {278 } else blk: {
302 const sym = zld.getSymbol(symbol);279 const sym = macho_file.getSymbol(symbol);
303 if (sym.n_desc == N_DEAD) continue;280 if (sym.n_desc == MachO.N_DEAD) continue;
304 if (prev_symbol) |prev_sym| {281 if (prev_symbol) |prev_sym| {
305 const prev_addr = object.getSourceSymbol(prev_sym.sym_index).?.n_value;282 const prev_addr = object.getSourceSymbol(prev_sym.sym_index).?.n_value;
306 const curr_addr = object.getSourceSymbol(symbol.sym_index).?.n_value;283 const curr_addr = object.getSourceSymbol(symbol.sym_index).?.n_value;
...@@ -311,7 +288,7 @@ pub fn collect(info: *UnwindInfo, zld: *Zld) !void {...@@ -311,7 +288,7 @@ pub fn collect(info: *UnwindInfo, zld: *Zld) !void {
311 if (object.eh_frame_records_lookup.get(symbol)) |fde_offset| {288 if (object.eh_frame_records_lookup.get(symbol)) |fde_offset| {
312 if (object.eh_frame_relocs_lookup.get(fde_offset).?.dead) continue;289 if (object.eh_frame_relocs_lookup.get(fde_offset).?.dead) continue;
313 var record = nullRecord();290 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);
315 switch (cpu_arch) {292 switch (cpu_arch) {
316 .aarch64 => UnwindEncoding.setMode(&record.compactUnwindEncoding, macho.UNWIND_ARM64_MODE.DWARF),293 .aarch64 => UnwindEncoding.setMode(&record.compactUnwindEncoding, macho.UNWIND_ARM64_MODE.DWARF),
317 .x86_64 => UnwindEncoding.setMode(&record.compactUnwindEncoding, macho.UNWIND_X86_64_MODE.DWARF),294 .x86_64 => UnwindEncoding.setMode(&record.compactUnwindEncoding, macho.UNWIND_X86_64_MODE.DWARF),
...@@ -324,9 +301,9 @@ pub fn collect(info: *UnwindInfo, zld: *Zld) !void {...@@ -324,9 +301,9 @@ pub fn collect(info: *UnwindInfo, zld: *Zld) !void {
324 break :blk nullRecord();301 break :blk nullRecord();
325 };302 };
326303
327 const atom = zld.getAtom(atom_index);304 const atom = macho_file.getAtom(atom_index);
328 const sym = zld.getSymbol(symbol);305 const sym = macho_file.getSymbol(symbol);
329 assert(sym.n_desc != N_DEAD);306 assert(sym.n_desc != MachO.N_DEAD);
330 const size = if (inner_syms_it.next()) |next_sym| blk: {307 const size = if (inner_syms_it.next()) |next_sym| blk: {
331 // All this trouble to account for symbol aliases.308 // All this trouble to account for symbol aliases.
332 // TODO I think that remodelling the linker so that a Symbol references an Atom309 // 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 {...@@ -337,8 +314,8 @@ pub fn collect(info: *UnwindInfo, zld: *Zld) !void {
337 const curr_addr = object.getSourceSymbol(symbol.sym_index).?.n_value;314 const curr_addr = object.getSourceSymbol(symbol.sym_index).?.n_value;
338 const next_addr = object.getSourceSymbol(next_sym.sym_index).?.n_value;315 const next_addr = object.getSourceSymbol(next_sym.sym_index).?.n_value;
339 if (next_addr > curr_addr) break :blk next_addr - curr_addr;316 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;317 break :blk macho_file.getSymbol(atom.getSymbolWithLoc()).n_value + atom.size - sym.n_value;
341 } else zld.getSymbol(atom.getSymbolWithLoc()).n_value + atom.size - sym.n_value;318 } else macho_file.getSymbol(atom.getSymbolWithLoc()).n_value + atom.size - sym.n_value;
342 record.rangeStart = sym.n_value;319 record.rangeStart = sym.n_value;
343 record.rangeLength = @as(u32, @intCast(size));320 record.rangeLength = @as(u32, @intCast(size));
344321
...@@ -519,23 +496,23 @@ pub fn collect(info: *UnwindInfo, zld: *Zld) !void {...@@ -519,23 +496,23 @@ pub fn collect(info: *UnwindInfo, zld: *Zld) !void {
519496
520fn collectPersonalityFromDwarf(497fn collectPersonalityFromDwarf(
521 info: *UnwindInfo,498 info: *UnwindInfo,
522 zld: *Zld,499 macho_file: *MachO,
523 object_id: u32,500 object_id: u32,
524 sym_loc: SymbolWithLoc,501 sym_loc: SymbolWithLoc,
525 record: *macho.compact_unwind_entry,502 record: *macho.compact_unwind_entry,
526) !void {503) void {
527 const object = &zld.objects.items[object_id];504 const object = &macho_file.objects.items[object_id];
528 var it = object.getEhFrameRecordsIterator();505 var it = object.getEhFrameRecordsIterator();
529 const fde_offset = object.eh_frame_records_lookup.get(sym_loc).?;506 const fde_offset = object.eh_frame_records_lookup.get(sym_loc).?;
530 it.seekTo(fde_offset);507 it.seekTo(fde_offset);
531 const fde = (try it.next()).?;508 const fde = (it.next() catch return).?; // We don't care about the error since we already handled it
532 const cie_ptr = fde.getCiePointerSource(object_id, zld, fde_offset);509 const cie_ptr = fde.getCiePointerSource(object_id, macho_file, fde_offset);
533 const cie_offset = fde_offset + 4 - cie_ptr;510 const cie_offset = fde_offset + 4 - cie_ptr;
534 it.seekTo(cie_offset);511 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
537 if (cie.getPersonalityPointerReloc(514 if (cie.getPersonalityPointerReloc(
538 zld,515 macho_file,
539 @as(u32, @intCast(object_id)),516 @as(u32, @intCast(object_id)),
540 cie_offset,517 cie_offset,
541 )) |target| {518 )) |target| {
...@@ -551,9 +528,9 @@ fn collectPersonalityFromDwarf(...@@ -551,9 +528,9 @@ fn collectPersonalityFromDwarf(
551 }528 }
552}529}
553530
554pub fn calcSectionSize(info: UnwindInfo, zld: *Zld) !void {531pub fn calcSectionSize(info: UnwindInfo, macho_file: *MachO) void {
555 const sect_id = zld.getSectionByName("__TEXT", "__unwind_info") orelse return;532 const sect_id = macho_file.unwind_info_section_index orelse return;
556 const sect = &zld.sections.items(.header)[sect_id];533 const sect = &macho_file.sections.items(.header)[sect_id];
557 sect.@"align" = 2;534 sect.@"align" = 2;
558 sect.size = info.calcRequiredSize();535 sect.size = info.calcRequiredSize();
559}536}
...@@ -570,25 +547,23 @@ fn calcRequiredSize(info: UnwindInfo) usize {...@@ -570,25 +547,23 @@ fn calcRequiredSize(info: UnwindInfo) usize {
570 return total_size;547 return total_size;
571}548}
572549
573pub fn write(info: *UnwindInfo, zld: *Zld) !void {550pub fn write(info: *UnwindInfo, macho_file: *MachO) !void {
574 const sect_id = zld.getSectionByName("__TEXT", "__unwind_info") orelse return;551 const sect_id = macho_file.unwind_info_section_index orelse return;
575 const sect = &zld.sections.items(.header)[sect_id];552 const sect = &macho_file.sections.items(.header)[sect_id];
576 const seg_id = zld.sections.items(.segment_index)[sect_id];553 const seg_id = macho_file.sections.items(.segment_index)[sect_id];
577 const seg = zld.segments.items[seg_id];554 const seg = macho_file.segments.items[seg_id];
578555
579 const text_sect_id = zld.getSectionByName("__TEXT", "__text").?;556 const text_sect_id = macho_file.text_section_index.?;
580 const text_sect = zld.sections.items(.header)[text_sect_id];557 const text_sect = macho_file.sections.items(.header)[text_sect_id];
581558
582 var personalities: [max_personalities]u32 = undefined;559 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
585 log.debug("Personalities:", .{});562 log.debug("Personalities:", .{});
586 for (info.personalities[0..info.personalities_count], 0..) |target, i| {563 for (info.personalities[0..info.personalities_count], 0..) |target, i| {
587 const atom_index = zld.getGotAtomIndexForSymbol(target).?;564 const addr = macho_file.getGotEntryAddress(target).?;
588 const atom = zld.getAtom(atom_index);565 personalities[i] = @as(u32, @intCast(addr - seg.vmaddr));
589 const sym = zld.getSymbol(atom.getSymbolWithLoc());566 log.debug(" {d}: 0x{x} ({s})", .{ i, personalities[i], macho_file.getSymbolName(target) });
590 personalities[i] = @as(u32, @intCast(sym.n_value - seg.vmaddr));
591 log.debug(" {d}: 0x{x} ({s})", .{ i, personalities[i], zld.getSymbolName(target) });
592 }567 }
593568
594 for (info.records.items) |*rec| {569 for (info.records.items) |*rec| {
...@@ -602,7 +577,7 @@ pub fn write(info: *UnwindInfo, zld: *Zld) !void {...@@ -602,7 +577,7 @@ pub fn write(info: *UnwindInfo, zld: *Zld) !void {
602 if (rec.compactUnwindEncoding > 0 and !UnwindEncoding.isDwarf(rec.compactUnwindEncoding, cpu_arch)) {577 if (rec.compactUnwindEncoding > 0 and !UnwindEncoding.isDwarf(rec.compactUnwindEncoding, cpu_arch)) {
603 const lsda_target = @as(SymbolWithLoc, @bitCast(rec.lsda));578 const lsda_target = @as(SymbolWithLoc, @bitCast(rec.lsda));
604 if (lsda_target.getFile()) |_| {579 if (lsda_target.getFile()) |_| {
605 const sym = zld.getSymbol(lsda_target);580 const sym = macho_file.getSymbol(lsda_target);
606 rec.lsda = sym.n_value - seg.vmaddr;581 rec.lsda = sym.n_value - seg.vmaddr;
607 }582 }
608 }583 }
...@@ -692,11 +667,11 @@ pub fn write(info: *UnwindInfo, zld: *Zld) !void {...@@ -692,11 +667,11 @@ pub fn write(info: *UnwindInfo, zld: *Zld) !void {
692 @memset(buffer.items[offset..], 0);667 @memset(buffer.items[offset..], 0);
693 }668 }
694669
695 try zld.file.pwriteAll(buffer.items, sect.offset);670 try macho_file.base.file.?.pwriteAll(buffer.items, sect.offset);
696}671}
697672
698fn getRelocs(zld: *Zld, object_id: u32, record_id: usize) []const macho.relocation_info {673fn getRelocs(macho_file: *MachO, object_id: u32, record_id: usize) []const macho.relocation_info {
699 const object = &zld.objects.items[object_id];674 const object = &macho_file.objects.items[object_id];
700 assert(object.hasUnwindRecords());675 assert(object.hasUnwindRecords());
701 const rel_pos = object.unwind_relocs_lookup[record_id].reloc;676 const rel_pos = object.unwind_relocs_lookup[record_id].reloc;
702 const relocs = object.getRelocs(object.unwind_info_sect_id.?);677 const relocs = object.getRelocs(object.unwind_info_sect_id.?);
...@@ -710,11 +685,11 @@ fn isPersonalityFunction(record_id: usize, rel: macho.relocation_info) bool {...@@ -710,11 +685,11 @@ fn isPersonalityFunction(record_id: usize, rel: macho.relocation_info) bool {
710}685}
711686
712pub fn getPersonalityFunctionReloc(687pub fn getPersonalityFunctionReloc(
713 zld: *Zld,688 macho_file: *MachO,
714 object_id: u32,689 object_id: u32,
715 record_id: usize,690 record_id: usize,
716) ?macho.relocation_info {691) ?macho.relocation_info {
717 const relocs = getRelocs(zld, object_id, record_id);692 const relocs = getRelocs(macho_file, object_id, record_id);
718 for (relocs) |rel| {693 for (relocs) |rel| {
719 if (isPersonalityFunction(record_id, rel)) return rel;694 if (isPersonalityFunction(record_id, rel)) return rel;
720 }695 }
...@@ -738,8 +713,8 @@ fn isLsda(record_id: usize, rel: macho.relocation_info) bool {...@@ -738,8 +713,8 @@ fn isLsda(record_id: usize, rel: macho.relocation_info) bool {
738 return rel_offset == 24;713 return rel_offset == 24;
739}714}
740715
741pub fn getLsdaReloc(zld: *Zld, object_id: u32, record_id: usize) ?macho.relocation_info {716pub fn getLsdaReloc(macho_file: *MachO, object_id: u32, record_id: usize) ?macho.relocation_info {
742 const relocs = getRelocs(zld, object_id, record_id);717 const relocs = getRelocs(macho_file, object_id, record_id);
743 for (relocs) |rel| {718 for (relocs) |rel| {
744 if (isLsda(record_id, rel)) return rel;719 if (isLsda(record_id, rel)) return rel;
745 }720 }
...@@ -831,3 +806,23 @@ pub const UnwindEncoding = struct {...@@ -831,3 +806,23 @@ pub const UnwindEncoding = struct {
831 enc.* |= offset;806 enc.* |= offset;
832 }807 }
833};808};
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 @@...@@ -1,92 +1,73 @@
1//! An algorithm for dead stripping of unreferenced Atoms.1//! An algorithm for dead stripping of unreferenced Atoms.
22
3const std = @import("std");3pub fn gcAtoms(macho_file: *MachO) !void {
4const assert = std.debug.assert;4 const gpa = macho_file.base.allocator;
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;
255
26 var arena = std.heap.ArenaAllocator.init(gpa);6 var arena = std.heap.ArenaAllocator.init(gpa);
27 defer arena.deinit();7 defer arena.deinit();
288
29 var roots = AtomTable.init(arena.allocator());9 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
32 var alive = AtomTable.init(arena.allocator());12 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);15 try collectRoots(macho_file, &roots);
36 try mark(zld, roots, &alive);16 mark(macho_file, roots, &alive);
37 prune(zld, alive);17 prune(macho_file, alive);
38}18}
3919
40fn addRoot(zld: *Zld, roots: *AtomTable, file: u32, sym_loc: SymbolWithLoc) !void {20fn addRoot(macho_file: *MachO, roots: *AtomTable, file: u32, sym_loc: SymbolWithLoc) !void {
41 const sym = zld.getSymbol(sym_loc);21 const sym = macho_file.getSymbol(sym_loc);
42 assert(!sym.undf());22 assert(!sym.undf());
43 const object = &zld.objects.items[file];23 const object = &macho_file.objects.items[file];
44 const atom_index = object.getAtomIndexForSymbol(sym_loc.sym_index).?; // panic here means fatal error24 const atom_index = object.getAtomIndexForSymbol(sym_loc.sym_index).?; // panic here means fatal error
45 log.debug("root(ATOM({d}, %{d}, {d}))", .{25 log.debug("root(ATOM({d}, %{d}, {d}))", .{
46 atom_index,26 atom_index,
47 zld.getAtom(atom_index).sym_index,27 macho_file.getAtom(atom_index).sym_index,
48 file,28 file,
49 });29 });
50 _ = try roots.getOrPut(atom_index);30 _ = try roots.getOrPut(atom_index);
51}31}
5232
53fn collectRoots(zld: *Zld, roots: *AtomTable, resolver: *const SymbolResolver) !void {33fn collectRoots(macho_file: *MachO, roots: *AtomTable) !void {
54 log.debug("collecting roots", .{});34 log.debug("collecting roots", .{});
5535
56 switch (zld.options.output_mode) {36 switch (macho_file.base.options.output_mode) {
57 .Exe => {37 .Exe => {
58 // Add entrypoint as GC root38 // Add entrypoint as GC root
59 const global: SymbolWithLoc = zld.getEntryPoint();39 if (macho_file.getEntryPoint()) |global| {
60 if (global.getFile()) |file| {40 if (global.getFile()) |file| {
61 try addRoot(zld, roots, file, global);41 try addRoot(macho_file, roots, file, global);
62 } else {42 } else {
63 assert(zld.getSymbol(global).undf()); // Stub as our entrypoint is in a dylib.43 assert(macho_file.getSymbol(global).undf()); // Stub as our entrypoint is in a dylib.
44 }
64 }45 }
65 },46 },
66 else => |other| {47 else => |other| {
67 assert(other == .Lib);48 assert(other == .Lib);
68 // Add exports as GC roots49 // Add exports as GC roots
69 for (zld.globals.items) |global| {50 for (macho_file.globals.items) |global| {
70 const sym = zld.getSymbol(global);51 const sym = macho_file.getSymbol(global);
71 if (sym.undf()) continue;52 if (sym.undf()) continue;
7253
73 if (global.getFile()) |file| {54 if (global.getFile()) |file| {
74 try addRoot(zld, roots, file, global);55 try addRoot(macho_file, roots, file, global);
75 }56 }
76 }57 }
77 },58 },
78 }59 }
7960
80 // Add all symbols force-defined by the user.61 // Add all symbols force-defined by the user.
81 for (zld.options.force_undefined_symbols.keys()) |sym_name| {62 for (macho_file.base.options.force_undefined_symbols.keys()) |sym_name| {
82 const global_index = resolver.table.get(sym_name).?;63 const global_index = macho_file.resolver.get(sym_name).?;
83 const global = zld.globals.items[global_index];64 const global = macho_file.globals.items[global_index];
84 const sym = zld.getSymbol(global);65 const sym = macho_file.getSymbol(global);
85 assert(!sym.undf());66 assert(!sym.undf());
86 try addRoot(zld, roots, global.getFile().?, global);67 try addRoot(macho_file, roots, global.getFile().?, global);
87 }68 }
8869
89 for (zld.objects.items) |object| {70 for (macho_file.objects.items) |object| {
90 const has_subsections = object.header.flags & macho.MH_SUBSECTIONS_VIA_SYMBOLS != 0;71 const has_subsections = object.header.flags & macho.MH_SUBSECTIONS_VIA_SYMBOLS != 0;
9172
92 for (object.atoms.items) |atom_index| {73 for (object.atoms.items) |atom_index| {
...@@ -95,7 +76,7 @@ fn collectRoots(zld: *Zld, roots: *AtomTable, resolver: *const SymbolResolver) !...@@ -95,7 +76,7 @@ fn collectRoots(zld: *Zld, roots: *AtomTable, resolver: *const SymbolResolver) !
95 // as a root.76 // as a root.
96 if (!has_subsections) break :blk true;77 if (!has_subsections) break :blk true;
9778
98 const atom = zld.getAtom(atom_index);79 const atom = macho_file.getAtom(atom_index);
99 const sect_id = if (object.getSourceSymbol(atom.sym_index)) |source_sym|80 const sect_id = if (object.getSourceSymbol(atom.sym_index)) |source_sym|
100 source_sym.n_sect - 181 source_sym.n_sect - 1
101 else sect_id: {82 else sect_id: {
...@@ -118,39 +99,39 @@ fn collectRoots(zld: *Zld, roots: *AtomTable, resolver: *const SymbolResolver) !...@@ -118,39 +99,39 @@ fn collectRoots(zld: *Zld, roots: *AtomTable, resolver: *const SymbolResolver) !
11899
119 log.debug("root(ATOM({d}, %{d}, {?d}))", .{100 log.debug("root(ATOM({d}, %{d}, {?d}))", .{
120 atom_index,101 atom_index,
121 zld.getAtom(atom_index).sym_index,102 macho_file.getAtom(atom_index).sym_index,
122 zld.getAtom(atom_index).getFile(),103 macho_file.getAtom(atom_index).getFile(),
123 });104 });
124 }105 }
125 }106 }
126 }107 }
127}108}
128109
129fn markLive(zld: *Zld, atom_index: AtomIndex, alive: *AtomTable) void {110fn markLive(macho_file: *MachO, atom_index: Atom.Index, alive: *AtomTable) void {
130 if (alive.contains(atom_index)) return;111 if (alive.contains(atom_index)) return;
131112
132 const atom = zld.getAtom(atom_index);113 const atom = macho_file.getAtom(atom_index);
133 const sym_loc = atom.getSymbolWithLoc();114 const sym_loc = atom.getSymbolWithLoc();
134115
135 log.debug("mark(ATOM({d}, %{d}, {?d}))", .{ atom_index, sym_loc.sym_index, sym_loc.getFile() });116 log.debug("mark(ATOM({d}, %{d}, {?d}))", .{ atom_index, sym_loc.sym_index, sym_loc.getFile() });
136117
137 alive.putAssumeCapacityNoClobber(atom_index, {});118 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());122 const sym = macho_file.getSymbol(atom.getSymbolWithLoc());
142 const header = zld.sections.items(.header)[sym.n_sect - 1];123 const header = macho_file.sections.items(.header)[sym.n_sect - 1];
143 if (header.isZerofill()) return;124 if (header.isZerofill()) return;
144125
145 const code = Atom.getAtomCode(zld, atom_index);126 const code = Atom.getAtomCode(macho_file, atom_index);
146 const relocs = Atom.getAtomRelocs(zld, atom_index);127 const relocs = Atom.getAtomRelocs(macho_file, atom_index);
147 const ctx = Atom.getRelocContext(zld, atom_index);128 const ctx = Atom.getRelocContext(macho_file, atom_index);
148129
149 for (relocs) |rel| {130 for (relocs) |rel| {
150 const target = switch (cpu_arch) {131 const target = switch (cpu_arch) {
151 .aarch64 => switch (@as(macho.reloc_type_arm64, @enumFromInt(rel.r_type))) {132 .aarch64 => switch (@as(macho.reloc_type_arm64, @enumFromInt(rel.r_type))) {
152 .ARM64_RELOC_ADDEND => continue,133 .ARM64_RELOC_ADDEND => continue,
153 else => Atom.parseRelocTarget(zld, .{134 else => Atom.parseRelocTarget(macho_file, .{
154 .object_id = atom.getFile().?,135 .object_id = atom.getFile().?,
155 .rel = rel,136 .rel = rel,
156 .code = code,137 .code = code,
...@@ -158,7 +139,7 @@ fn markLive(zld: *Zld, atom_index: AtomIndex, alive: *AtomTable) void {...@@ -158,7 +139,7 @@ fn markLive(zld: *Zld, atom_index: AtomIndex, alive: *AtomTable) void {
158 .base_addr = ctx.base_addr,139 .base_addr = ctx.base_addr,
159 }),140 }),
160 },141 },
161 .x86_64 => Atom.parseRelocTarget(zld, .{142 .x86_64 => Atom.parseRelocTarget(macho_file, .{
162 .object_id = atom.getFile().?,143 .object_id = atom.getFile().?,
163 .rel = rel,144 .rel = rel,
164 .code = code,145 .code = code,
...@@ -167,50 +148,50 @@ fn markLive(zld: *Zld, atom_index: AtomIndex, alive: *AtomTable) void {...@@ -167,50 +148,50 @@ fn markLive(zld: *Zld, atom_index: AtomIndex, alive: *AtomTable) void {
167 }),148 }),
168 else => unreachable,149 else => unreachable,
169 };150 };
170 const target_sym = zld.getSymbol(target);151 const target_sym = macho_file.getSymbol(target);
171152
172 if (target_sym.undf()) continue;153 if (target_sym.undf()) continue;
173 if (target.getFile() == null) {154 if (target.getFile() == null) {
174 const target_sym_name = zld.getSymbolName(target);155 const target_sym_name = macho_file.getSymbolName(target);
175 if (mem.eql(u8, "__mh_execute_header", target_sym_name)) continue;156 if (mem.eql(u8, "__mh_execute_header", target_sym_name)) continue;
176 if (mem.eql(u8, "___dso_handle", target_sym_name)) continue;157 if (mem.eql(u8, "___dso_handle", target_sym_name)) continue;
177158
178 unreachable; // referenced symbol not found159 unreachable; // referenced symbol not found
179 }160 }
180161
181 const object = zld.objects.items[target.getFile().?];162 const object = macho_file.objects.items[target.getFile().?];
182 const target_atom_index = object.getAtomIndexForSymbol(target.sym_index).?;163 const target_atom_index = object.getAtomIndexForSymbol(target.sym_index).?;
183 log.debug(" following ATOM({d}, %{d}, {?d})", .{164 log.debug(" following ATOM({d}, %{d}, {?d})", .{
184 target_atom_index,165 target_atom_index,
185 zld.getAtom(target_atom_index).sym_index,166 macho_file.getAtom(target_atom_index).sym_index,
186 zld.getAtom(target_atom_index).getFile(),167 macho_file.getAtom(target_atom_index).getFile(),
187 });168 });
188169
189 markLive(zld, target_atom_index, alive);170 markLive(macho_file, target_atom_index, alive);
190 }171 }
191}172}
192173
193fn refersLive(zld: *Zld, atom_index: AtomIndex, alive: AtomTable) bool {174fn refersLive(macho_file: *MachO, atom_index: Atom.Index, alive: AtomTable) bool {
194 const atom = zld.getAtom(atom_index);175 const atom = macho_file.getAtom(atom_index);
195 const sym_loc = atom.getSymbolWithLoc();176 const sym_loc = atom.getSymbolWithLoc();
196177
197 log.debug("refersLive(ATOM({d}, %{d}, {?d}))", .{ atom_index, sym_loc.sym_index, sym_loc.getFile() });178 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);182 const sym = macho_file.getSymbol(sym_loc);
202 const header = zld.sections.items(.header)[sym.n_sect - 1];183 const header = macho_file.sections.items(.header)[sym.n_sect - 1];
203 assert(!header.isZerofill());184 assert(!header.isZerofill());
204185
205 const code = Atom.getAtomCode(zld, atom_index);186 const code = Atom.getAtomCode(macho_file, atom_index);
206 const relocs = Atom.getAtomRelocs(zld, atom_index);187 const relocs = Atom.getAtomRelocs(macho_file, atom_index);
207 const ctx = Atom.getRelocContext(zld, atom_index);188 const ctx = Atom.getRelocContext(macho_file, atom_index);
208189
209 for (relocs) |rel| {190 for (relocs) |rel| {
210 const target = switch (cpu_arch) {191 const target = switch (cpu_arch) {
211 .aarch64 => switch (@as(macho.reloc_type_arm64, @enumFromInt(rel.r_type))) {192 .aarch64 => switch (@as(macho.reloc_type_arm64, @enumFromInt(rel.r_type))) {
212 .ARM64_RELOC_ADDEND => continue,193 .ARM64_RELOC_ADDEND => continue,
213 else => Atom.parseRelocTarget(zld, .{194 else => Atom.parseRelocTarget(macho_file, .{
214 .object_id = atom.getFile().?,195 .object_id = atom.getFile().?,
215 .rel = rel,196 .rel = rel,
216 .code = code,197 .code = code,
...@@ -218,7 +199,7 @@ fn refersLive(zld: *Zld, atom_index: AtomIndex, alive: AtomTable) bool {...@@ -218,7 +199,7 @@ fn refersLive(zld: *Zld, atom_index: AtomIndex, alive: AtomTable) bool {
218 .base_addr = ctx.base_addr,199 .base_addr = ctx.base_addr,
219 }),200 }),
220 },201 },
221 .x86_64 => Atom.parseRelocTarget(zld, .{202 .x86_64 => Atom.parseRelocTarget(macho_file, .{
222 .object_id = atom.getFile().?,203 .object_id = atom.getFile().?,
223 .rel = rel,204 .rel = rel,
224 .code = code,205 .code = code,
...@@ -228,16 +209,16 @@ fn refersLive(zld: *Zld, atom_index: AtomIndex, alive: AtomTable) bool {...@@ -228,16 +209,16 @@ fn refersLive(zld: *Zld, atom_index: AtomIndex, alive: AtomTable) bool {
228 else => unreachable,209 else => unreachable,
229 };210 };
230211
231 const object = zld.objects.items[target.getFile().?];212 const object = macho_file.objects.items[target.getFile().?];
232 const target_atom_index = object.getAtomIndexForSymbol(target.sym_index) orelse {213 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)});
234 continue;215 continue;
235 };216 };
236 if (alive.contains(target_atom_index)) {217 if (alive.contains(target_atom_index)) {
237 log.debug(" refers live ATOM({d}, %{d}, {?d})", .{218 log.debug(" refers live ATOM({d}, %{d}, {?d})", .{
238 target_atom_index,219 target_atom_index,
239 zld.getAtom(target_atom_index).sym_index,220 macho_file.getAtom(target_atom_index).sym_index,
240 zld.getAtom(target_atom_index).getFile(),221 macho_file.getAtom(target_atom_index).getFile(),
241 });222 });
242 return true;223 return true;
243 }224 }
...@@ -246,21 +227,21 @@ fn refersLive(zld: *Zld, atom_index: AtomIndex, alive: AtomTable) bool {...@@ -246,21 +227,21 @@ fn refersLive(zld: *Zld, atom_index: AtomIndex, alive: AtomTable) bool {
246 return false;227 return false;
247}228}
248229
249fn mark(zld: *Zld, roots: AtomTable, alive: *AtomTable) !void {230fn mark(macho_file: *MachO, roots: AtomTable, alive: *AtomTable) void {
250 var it = roots.keyIterator();231 var it = roots.keyIterator();
251 while (it.next()) |root| {232 while (it.next()) |root| {
252 markLive(zld, root.*, alive);233 markLive(macho_file, root.*, alive);
253 }234 }
254235
255 var loop: bool = true;236 var loop: bool = true;
256 while (loop) {237 while (loop) {
257 loop = false;238 loop = false;
258239
259 for (zld.objects.items) |object| {240 for (macho_file.objects.items) |object| {
260 for (object.atoms.items) |atom_index| {241 for (object.atoms.items) |atom_index| {
261 if (alive.contains(atom_index)) continue;242 if (alive.contains(atom_index)) continue;
262243
263 const atom = zld.getAtom(atom_index);244 const atom = macho_file.getAtom(atom_index);
264 const sect_id = if (object.getSourceSymbol(atom.sym_index)) |source_sym|245 const sect_id = if (object.getSourceSymbol(atom.sym_index)) |source_sym|
265 source_sym.n_sect - 1246 source_sym.n_sect - 1
266 else blk: {247 else blk: {
...@@ -271,8 +252,8 @@ fn mark(zld: *Zld, roots: AtomTable, alive: *AtomTable) !void {...@@ -271,8 +252,8 @@ fn mark(zld: *Zld, roots: AtomTable, alive: *AtomTable) !void {
271 const source_sect = object.getSourceSection(sect_id);252 const source_sect = object.getSourceSection(sect_id);
272253
273 if (source_sect.isDontDeadStripIfReferencesLive()) {254 if (source_sect.isDontDeadStripIfReferencesLive()) {
274 if (refersLive(zld, atom_index, alive.*)) {255 if (refersLive(macho_file, atom_index, alive.*)) {
275 markLive(zld, atom_index, alive);256 markLive(macho_file, atom_index, alive);
276 loop = true;257 loop = true;
277 }258 }
278 }259 }
...@@ -280,26 +261,26 @@ fn mark(zld: *Zld, roots: AtomTable, alive: *AtomTable) !void {...@@ -280,26 +261,26 @@ fn mark(zld: *Zld, roots: AtomTable, alive: *AtomTable) !void {
280 }261 }
281 }262 }
282263
283 for (zld.objects.items, 0..) |_, object_id| {264 for (macho_file.objects.items, 0..) |_, object_id| {
284 // Traverse unwind and eh_frame records noting if the source symbol has been marked, and if so,265 // Traverse unwind and eh_frame records noting if the source symbol has been marked, and if so,
285 // marking all references as live.266 // 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);
287 }268 }
288}269}
289270
290fn markUnwindRecords(zld: *Zld, object_id: u32, alive: *AtomTable) !void {271fn markUnwindRecords(macho_file: *MachO, object_id: u32, alive: *AtomTable) void {
291 const object = &zld.objects.items[object_id];272 const object = &macho_file.objects.items[object_id];
292 const cpu_arch = zld.options.target.cpu.arch;273 const cpu_arch = macho_file.base.options.target.cpu.arch;
293274
294 const unwind_records = object.getUnwindRecords();275 const unwind_records = object.getUnwindRecords();
295276
296 for (object.exec_atoms.items) |atom_index| {277 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
299 if (!object.hasUnwindRecords()) {280 if (!object.hasUnwindRecords()) {
300 if (alive.contains(atom_index)) {281 if (alive.contains(atom_index)) {
301 // Mark references live and continue.282 // Mark references live and continue.
302 try markEhFrameRecords(zld, object_id, atom_index, alive);283 markEhFrameRecords(macho_file, object_id, atom_index, alive);
303 } else {284 } else {
304 while (inner_syms_it.next()) |sym| {285 while (inner_syms_it.next()) |sym| {
305 if (object.eh_frame_records_lookup.get(sym)) |fde_offset| {286 if (object.eh_frame_records_lookup.get(sym)) |fde_offset| {
...@@ -325,86 +306,86 @@ fn markUnwindRecords(zld: *Zld, object_id: u32, alive: *AtomTable) !void {...@@ -325,86 +306,86 @@ fn markUnwindRecords(zld: *Zld, object_id: u32, alive: *AtomTable) !void {
325306
326 const record = unwind_records[record_id];307 const record = unwind_records[record_id];
327 if (UnwindInfo.UnwindEncoding.isDwarf(record.compactUnwindEncoding, cpu_arch)) {308 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);
329 } else {310 } else {
330 if (UnwindInfo.getPersonalityFunctionReloc(zld, object_id, record_id)) |rel| {311 if (UnwindInfo.getPersonalityFunctionReloc(macho_file, object_id, record_id)) |rel| {
331 const target = Atom.parseRelocTarget(zld, .{312 const target = Atom.parseRelocTarget(macho_file, .{
332 .object_id = object_id,313 .object_id = object_id,
333 .rel = rel,314 .rel = rel,
334 .code = mem.asBytes(&record),315 .code = mem.asBytes(&record),
335 .base_offset = @as(i32, @intCast(record_id * @sizeOf(macho.compact_unwind_entry))),316 .base_offset = @as(i32, @intCast(record_id * @sizeOf(macho.compact_unwind_entry))),
336 });317 });
337 const target_sym = zld.getSymbol(target);318 const target_sym = macho_file.getSymbol(target);
338 if (!target_sym.undf()) {319 if (!target_sym.undf()) {
339 const target_object = zld.objects.items[target.getFile().?];320 const target_object = macho_file.objects.items[target.getFile().?];
340 const target_atom_index = target_object.getAtomIndexForSymbol(target.sym_index).?;321 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);
342 }323 }
343 }324 }
344325
345 if (UnwindInfo.getLsdaReloc(zld, object_id, record_id)) |rel| {326 if (UnwindInfo.getLsdaReloc(macho_file, object_id, record_id)) |rel| {
346 const target = Atom.parseRelocTarget(zld, .{327 const target = Atom.parseRelocTarget(macho_file, .{
347 .object_id = object_id,328 .object_id = object_id,
348 .rel = rel,329 .rel = rel,
349 .code = mem.asBytes(&record),330 .code = mem.asBytes(&record),
350 .base_offset = @as(i32, @intCast(record_id * @sizeOf(macho.compact_unwind_entry))),331 .base_offset = @as(i32, @intCast(record_id * @sizeOf(macho.compact_unwind_entry))),
351 });332 });
352 const target_object = zld.objects.items[target.getFile().?];333 const target_object = macho_file.objects.items[target.getFile().?];
353 const target_atom_index = target_object.getAtomIndexForSymbol(target.sym_index).?;334 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);
355 }336 }
356 }337 }
357 }338 }
358 }339 }
359}340}
360341
361fn markEhFrameRecords(zld: *Zld, object_id: u32, atom_index: AtomIndex, alive: *AtomTable) !void {342fn markEhFrameRecords(macho_file: *MachO, object_id: u32, atom_index: Atom.Index, alive: *AtomTable) void {
362 const cpu_arch = zld.options.target.cpu.arch;343 const cpu_arch = macho_file.base.options.target.cpu.arch;
363 const object = &zld.objects.items[object_id];344 const object = &macho_file.objects.items[object_id];
364 var it = object.getEhFrameRecordsIterator();345 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
367 while (inner_syms_it.next()) |sym| {348 while (inner_syms_it.next()) |sym| {
368 const fde_offset = object.eh_frame_records_lookup.get(sym) orelse continue; // Continue in case we hit a temp symbol alias349 const fde_offset = object.eh_frame_records_lookup.get(sym) orelse continue; // Continue in case we hit a temp symbol alias
369 it.seekTo(fde_offset);350 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);
373 const cie_offset = fde_offset + 4 - cie_ptr;354 const cie_offset = fde_offset + 4 - cie_ptr;
374 it.seekTo(cie_offset);355 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
377 switch (cpu_arch) {358 switch (cpu_arch) {
378 .aarch64 => {359 .aarch64 => {
379 // Mark FDE references which should include any referenced LSDA record360 // 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);
381 for (relocs) |rel| {362 for (relocs) |rel| {
382 const target = Atom.parseRelocTarget(zld, .{363 const target = Atom.parseRelocTarget(macho_file, .{
383 .object_id = object_id,364 .object_id = object_id,
384 .rel = rel,365 .rel = rel,
385 .code = fde.data,366 .code = fde.data,
386 .base_offset = @as(i32, @intCast(fde_offset)) + 4,367 .base_offset = @as(i32, @intCast(fde_offset)) + 4,
387 });368 });
388 const target_sym = zld.getSymbol(target);369 const target_sym = macho_file.getSymbol(target);
389 if (!target_sym.undf()) blk: {370 if (!target_sym.undf()) blk: {
390 const target_object = zld.objects.items[target.getFile().?];371 const target_object = macho_file.objects.items[target.getFile().?];
391 const target_atom_index = target_object.getAtomIndexForSymbol(target.sym_index) orelse372 const target_atom_index = target_object.getAtomIndexForSymbol(target.sym_index) orelse
392 break :blk;373 break :blk;
393 markLive(zld, target_atom_index, alive);374 markLive(macho_file, target_atom_index, alive);
394 }375 }
395 }376 }
396 },377 },
397 .x86_64 => {378 .x86_64 => {
398 const sect = object.getSourceSection(object.eh_frame_sect_id.?);379 const sect = object.getSourceSection(object.eh_frame_sect_id.?);
399 const lsda_ptr = try fde.getLsdaPointer(cie, .{380 const lsda_ptr = fde.getLsdaPointer(cie, .{
400 .base_addr = sect.addr,381 .base_addr = sect.addr,
401 .base_offset = fde_offset,382 .base_offset = fde_offset,
402 });383 }) catch continue; // We don't care about the error at this point since it was already handled
403 if (lsda_ptr) |lsda_address| {384 if (lsda_ptr) |lsda_address| {
404 // Mark LSDA record as live385 // Mark LSDA record as live
405 const sym_index = object.getSymbolByAddress(lsda_address, null);386 const sym_index = object.getSymbolByAddress(lsda_address, null);
406 const target_atom_index = object.getAtomIndexForSymbol(sym_index).?;387 const target_atom_index = object.getAtomIndexForSymbol(sym_index).?;
407 markLive(zld, target_atom_index, alive);388 markLive(macho_file, target_atom_index, alive);
408 }389 }
409 },390 },
410 else => unreachable,391 else => unreachable,
...@@ -412,20 +393,20 @@ fn markEhFrameRecords(zld: *Zld, object_id: u32, atom_index: AtomIndex, alive: *...@@ -412,20 +393,20 @@ fn markEhFrameRecords(zld: *Zld, object_id: u32, atom_index: AtomIndex, alive: *
412393
413 // Mark CIE references which should include any referenced personalities394 // Mark CIE references which should include any referenced personalities
414 // that are defined locally.395 // that are defined locally.
415 if (cie.getPersonalityPointerReloc(zld, object_id, cie_offset)) |target| {396 if (cie.getPersonalityPointerReloc(macho_file, object_id, cie_offset)) |target| {
416 const target_sym = zld.getSymbol(target);397 const target_sym = macho_file.getSymbol(target);
417 if (!target_sym.undf()) {398 if (!target_sym.undf()) {
418 const target_object = zld.objects.items[target.getFile().?];399 const target_object = macho_file.objects.items[target.getFile().?];
419 const target_atom_index = target_object.getAtomIndexForSymbol(target.sym_index).?;400 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);
421 }402 }
422 }403 }
423 }404 }
424}405}
425406
426fn prune(zld: *Zld, alive: AtomTable) void {407fn prune(macho_file: *MachO, alive: AtomTable) void {
427 log.debug("pruning dead atoms", .{});408 log.debug("pruning dead atoms", .{});
428 for (zld.objects.items) |*object| {409 for (macho_file.objects.items) |*object| {
429 var i: usize = 0;410 var i: usize = 0;
430 while (i < object.atoms.items.len) {411 while (i < object.atoms.items.len) {
431 const atom_index = object.atoms.items[i];412 const atom_index = object.atoms.items[i];
...@@ -434,7 +415,7 @@ fn prune(zld: *Zld, alive: AtomTable) void {...@@ -434,7 +415,7 @@ fn prune(zld: *Zld, alive: AtomTable) void {
434 continue;415 continue;
435 }416 }
436417
437 const atom = zld.getAtom(atom_index);418 const atom = macho_file.getAtom(atom_index);
438 const sym_loc = atom.getSymbolWithLoc();419 const sym_loc = atom.getSymbolWithLoc();
439420
440 log.debug("prune(ATOM({d}, %{d}, {?d}))", .{421 log.debug("prune(ATOM({d}, %{d}, {?d}))", .{
...@@ -442,15 +423,15 @@ fn prune(zld: *Zld, alive: AtomTable) void {...@@ -442,15 +423,15 @@ fn prune(zld: *Zld, alive: AtomTable) void {
442 sym_loc.sym_index,423 sym_loc.sym_index,
443 sym_loc.getFile(),424 sym_loc.getFile(),
444 });425 });
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);
448 const sect_id = sym.n_sect - 1;429 const sect_id = sym.n_sect - 1;
449 var section = zld.sections.get(sect_id);430 var section = macho_file.sections.get(sect_id);
450 section.header.size -= atom.size;431 section.header.size -= atom.size;
451432
452 if (atom.prev_index) |prev_index| {433 if (atom.prev_index) |prev_index| {
453 const prev = zld.getAtomPtr(prev_index);434 const prev = macho_file.getAtomPtr(prev_index);
454 prev.next_index = atom.next_index;435 prev.next_index = atom.next_index;
455 } else {436 } else {
456 if (atom.next_index) |next_index| {437 if (atom.next_index) |next_index| {
...@@ -458,33 +439,49 @@ fn prune(zld: *Zld, alive: AtomTable) void {...@@ -458,33 +439,49 @@ fn prune(zld: *Zld, alive: AtomTable) void {
458 }439 }
459 }440 }
460 if (atom.next_index) |next_index| {441 if (atom.next_index) |next_index| {
461 const next = zld.getAtomPtr(next_index);442 const next = macho_file.getAtomPtr(next_index);
462 next.prev_index = atom.prev_index;443 next.prev_index = atom.prev_index;
463 } else {444 } else {
464 if (atom.prev_index) |prev_index| {445 if (atom.prev_index) |prev_index| {
465 section.last_atom_index = prev_index;446 section.last_atom_index = prev_index;
466 } else {447 } else {
467 assert(section.header.size == 0);448 assert(section.header.size == 0);
468 section.first_atom_index = 0;449 section.first_atom_index = null;
469 section.last_atom_index = 0;450 section.last_atom_index = null;
470 }451 }
471 }452 }
472453
473 zld.sections.set(sect_id, section);454 macho_file.sections.set(sect_id, section);
474 _ = object.atoms.swapRemove(i);455 _ = 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);
479 while (inner_sym_it.next()) |inner| {460 while (inner_sym_it.next()) |inner| {
480 const inner_sym = zld.getSymbolPtr(inner);461 const inner_sym = macho_file.getSymbolPtr(inner);
481 inner_sym.n_desc = N_DEAD;462 inner_sym.n_desc = MachO.N_DEAD;
482 }463 }
483464
484 if (Atom.getSectionAlias(zld, atom_index)) |alias| {465 if (Atom.getSectionAlias(macho_file, atom_index)) |alias| {
485 const alias_sym = zld.getSymbolPtr(alias);466 const alias_sym = macho_file.getSymbolPtr(alias);
486 alias_sym.n_desc = N_DEAD;467 alias_sym.n_desc = MachO.N_DEAD;
487 }468 }
488 }469 }
489 }470 }
490}471}
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 @@...@@ -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
12entries: std.ArrayListUnmanaged(Entry) = .{},1entries: std.ArrayListUnmanaged(Entry) = .{},
13buffer: std.ArrayListUnmanaged(u8) = .{},2buffer: std.ArrayListUnmanaged(u8) = .{},
143
...@@ -572,3 +561,14 @@ test "rebase - composite" {...@@ -572,3 +561,14 @@ test "rebase - composite" {
572 macho.REBASE_OPCODE_DONE,561 macho.REBASE_OPCODE_DONE,
573 }, rebase.buffer.items);562 }, rebase.buffer.items);
574}563}
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 @@...@@ -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
10pub fn Bind(comptime Ctx: type, comptime Target: type) type {1pub fn Bind(comptime Ctx: type, comptime Target: type) type {
11 return struct {2 return struct {
12 entries: std.ArrayListUnmanaged(Entry) = .{},3 entries: std.ArrayListUnmanaged(Entry) = .{},
...@@ -738,3 +729,12 @@ test "lazy bind" {...@@ -738,3 +729,12 @@ test "lazy bind" {
738 macho.BIND_OPCODE_DONE,729 macho.BIND_OPCODE_DONE,
739 }, bind.buffer.items);730 }, bind.buffer.items);
740}731}
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 @@...@@ -1,68 +1,52 @@
1const std = @import("std");1pub fn scanRelocs(macho_file: *MachO) !void {
2const assert = std.debug.assert;2 const gpa = macho_file.base.allocator;
3const macho = std.macho;
4const math = std.math;
5const mem = std.mem;
6const leb = std.leb;
7const log = std.log.scoped(.eh_frame);
83
9const Allocator = mem.Allocator;4 for (macho_file.objects.items, 0..) |*object, object_id| {
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| {
21 var cies = std.AutoHashMap(u32, void).init(gpa);5 var cies = std.AutoHashMap(u32, void).init(gpa);
22 defer cies.deinit();6 defer cies.deinit();
237
24 var it = object.getEhFrameRecordsIterator();8 var it = object.getEhFrameRecordsIterator();
259
26 for (object.exec_atoms.items) |atom_index| {10 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);
28 while (inner_syms_it.next()) |sym| {12 while (inner_syms_it.next()) |sym| {
29 const fde_offset = object.eh_frame_records_lookup.get(sym) orelse continue;13 const fde_offset = object.eh_frame_records_lookup.get(sym) orelse continue;
30 if (object.eh_frame_relocs_lookup.get(fde_offset).?.dead) continue;14 if (object.eh_frame_relocs_lookup.get(fde_offset).?.dead) continue;
31 it.seekTo(fde_offset);15 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);
35 const cie_offset = fde_offset + 4 - cie_ptr;19 const cie_offset = fde_offset + 4 - cie_ptr;
3620
37 if (!cies.contains(cie_offset)) {21 if (!cies.contains(cie_offset)) {
38 try cies.putNoClobber(cie_offset, {});22 try cies.putNoClobber(cie_offset, {});
39 it.seekTo(cie_offset);23 it.seekTo(cie_offset);
40 const cie = (try it.next()).?;24 const cie = (it.next() catch continue).?; // We don't care about this error since we already handled it
41 try cie.scanRelocs(zld, @as(u32, @intCast(object_id)), cie_offset);25 try cie.scanRelocs(macho_file, @as(u32, @intCast(object_id)), cie_offset);
42 }26 }
43 }27 }
44 }28 }
45 }29 }
46}30}
4731
48pub fn calcSectionSize(zld: *Zld, unwind_info: *const UnwindInfo) !void {32pub fn calcSectionSize(macho_file: *MachO, unwind_info: *const UnwindInfo) error{OutOfMemory}!void {
49 const sect_id = zld.getSectionByName("__TEXT", "__eh_frame") orelse return;33 const sect_id = macho_file.eh_frame_section_index orelse return;
50 const sect = &zld.sections.items(.header)[sect_id];34 const sect = &macho_file.sections.items(.header)[sect_id];
51 sect.@"align" = 3;35 sect.@"align" = 3;
52 sect.size = 0;36 sect.size = 0;
5337
54 const cpu_arch = zld.options.target.cpu.arch;38 const cpu_arch = macho_file.base.options.target.cpu.arch;
55 const gpa = zld.gpa;39 const gpa = macho_file.base.allocator;
56 var size: u32 = 0;40 var size: u32 = 0;
5741
58 for (zld.objects.items, 0..) |*object, object_id| {42 for (macho_file.objects.items, 0..) |*object, object_id| {
59 var cies = std.AutoHashMap(u32, u32).init(gpa);43 var cies = std.AutoHashMap(u32, u32).init(gpa);
60 defer cies.deinit();44 defer cies.deinit();
6145
62 var eh_it = object.getEhFrameRecordsIterator();46 var eh_it = object.getEhFrameRecordsIterator();
6347
64 for (object.exec_atoms.items) |atom_index| {48 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);
66 while (inner_syms_it.next()) |sym| {50 while (inner_syms_it.next()) |sym| {
67 const fde_record_offset = object.eh_frame_records_lookup.get(sym) orelse continue;51 const fde_record_offset = object.eh_frame_records_lookup.get(sym) orelse continue;
68 if (object.eh_frame_relocs_lookup.get(fde_record_offset).?.dead) continue;52 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 {...@@ -75,15 +59,15 @@ pub fn calcSectionSize(zld: *Zld, unwind_info: *const UnwindInfo) !void {
75 if (!is_dwarf) continue;59 if (!is_dwarf) continue;
7660
77 eh_it.seekTo(fde_record_offset);61 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);
81 const cie_offset = fde_record_offset + 4 - cie_ptr;65 const cie_offset = fde_record_offset + 4 - cie_ptr;
8266
83 const gop = try cies.getOrPut(cie_offset);67 const gop = try cies.getOrPut(cie_offset);
84 if (!gop.found_existing) {68 if (!gop.found_existing) {
85 eh_it.seekTo(cie_offset);69 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
87 gop.value_ptr.* = size;71 gop.value_ptr.* = size;
88 size += source_cie_record.getSize();72 size += source_cie_record.getSize();
89 }73 }
...@@ -96,14 +80,14 @@ pub fn calcSectionSize(zld: *Zld, unwind_info: *const UnwindInfo) !void {...@@ -96,14 +80,14 @@ pub fn calcSectionSize(zld: *Zld, unwind_info: *const UnwindInfo) !void {
96 }80 }
97}81}
9882
99pub fn write(zld: *Zld, unwind_info: *UnwindInfo) !void {83pub fn write(macho_file: *MachO, unwind_info: *UnwindInfo) !void {
100 const sect_id = zld.getSectionByName("__TEXT", "__eh_frame") orelse return;84 const sect_id = macho_file.eh_frame_section_index orelse return;
101 const sect = zld.sections.items(.header)[sect_id];85 const sect = macho_file.sections.items(.header)[sect_id];
102 const seg_id = zld.sections.items(.segment_index)[sect_id];86 const seg_id = macho_file.sections.items(.segment_index)[sect_id];
103 const seg = zld.segments.items[seg_id];87 const seg = macho_file.segments.items[seg_id];
10488
105 const cpu_arch = zld.options.target.cpu.arch;89 const cpu_arch = macho_file.base.options.target.cpu.arch;
106 const gpa = zld.gpa;90 const gpa = macho_file.base.allocator;
10791
108 var eh_records = std.AutoArrayHashMap(u32, EhFrameRecord(true)).init(gpa);92 var eh_records = std.AutoArrayHashMap(u32, EhFrameRecord(true)).init(gpa);
109 defer {93 defer {
...@@ -115,7 +99,7 @@ pub fn write(zld: *Zld, unwind_info: *UnwindInfo) !void {...@@ -115,7 +99,7 @@ pub fn write(zld: *Zld, unwind_info: *UnwindInfo) !void {
11599
116 var eh_frame_offset: u32 = 0;100 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| {
119 try eh_records.ensureUnusedCapacity(2 * @as(u32, @intCast(object.exec_atoms.items.len)));103 try eh_records.ensureUnusedCapacity(2 * @as(u32, @intCast(object.exec_atoms.items.len)));
120104
121 var cies = std.AutoHashMap(u32, u32).init(gpa);105 var cies = std.AutoHashMap(u32, u32).init(gpa);
...@@ -124,7 +108,7 @@ pub fn write(zld: *Zld, unwind_info: *UnwindInfo) !void {...@@ -124,7 +108,7 @@ pub fn write(zld: *Zld, unwind_info: *UnwindInfo) !void {
124 var eh_it = object.getEhFrameRecordsIterator();108 var eh_it = object.getEhFrameRecordsIterator();
125109
126 for (object.exec_atoms.items) |atom_index| {110 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);
128 while (inner_syms_it.next()) |target| {112 while (inner_syms_it.next()) |target| {
129 const fde_record_offset = object.eh_frame_records_lookup.get(target) orelse continue;113 const fde_record_offset = object.eh_frame_records_lookup.get(target) orelse continue;
130 if (object.eh_frame_relocs_lookup.get(fde_record_offset).?.dead) continue;114 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 {...@@ -137,17 +121,17 @@ pub fn write(zld: *Zld, unwind_info: *UnwindInfo) !void {
137 if (!is_dwarf) continue;121 if (!is_dwarf) continue;
138122
139 eh_it.seekTo(fde_record_offset);123 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);
143 const cie_offset = fde_record_offset + 4 - cie_ptr;127 const cie_offset = fde_record_offset + 4 - cie_ptr;
144128
145 const gop = try cies.getOrPut(cie_offset);129 const gop = try cies.getOrPut(cie_offset);
146 if (!gop.found_existing) {130 if (!gop.found_existing) {
147 eh_it.seekTo(cie_offset);131 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
149 var cie_record = try source_cie_record.toOwned(gpa);133 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)), .{
151 .source_offset = cie_offset,135 .source_offset = cie_offset,
152 .out_offset = eh_frame_offset,136 .out_offset = eh_frame_offset,
153 .sect_addr = sect.addr,137 .sect_addr = sect.addr,
...@@ -158,7 +142,7 @@ pub fn write(zld: *Zld, unwind_info: *UnwindInfo) !void {...@@ -158,7 +142,7 @@ pub fn write(zld: *Zld, unwind_info: *UnwindInfo) !void {
158 }142 }
159143
160 var fde_record = try source_fde_record.toOwned(gpa);144 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)), .{
162 .source_offset = fde_record_offset,146 .source_offset = fde_record_offset,
163 .out_offset = eh_frame_offset,147 .out_offset = eh_frame_offset,
164 .sect_addr = sect.addr,148 .sect_addr = sect.addr,
...@@ -169,7 +153,7 @@ pub fn write(zld: *Zld, unwind_info: *UnwindInfo) !void {...@@ -169,7 +153,7 @@ pub fn write(zld: *Zld, unwind_info: *UnwindInfo) !void {
169 .aarch64 => {}, // relocs take care of LSDA pointers153 .aarch64 => {}, // relocs take care of LSDA pointers
170 .x86_64 => {154 .x86_64 => {
171 // We need to relocate target symbol address ourselves.155 // We need to relocate target symbol address ourselves.
172 const atom_sym = zld.getSymbol(target);156 const atom_sym = macho_file.getSymbol(target);
173 try fde_record.setTargetSymbolAddress(atom_sym.n_value, .{157 try fde_record.setTargetSymbolAddress(atom_sym.n_value, .{
174 .base_addr = sect.addr,158 .base_addr = sect.addr,
175 .base_offset = eh_frame_offset,159 .base_offset = eh_frame_offset,
...@@ -180,17 +164,17 @@ pub fn write(zld: *Zld, unwind_info: *UnwindInfo) !void {...@@ -180,17 +164,17 @@ pub fn write(zld: *Zld, unwind_info: *UnwindInfo) !void {
180 eh_frame_offset + 4 - fde_record.getCiePointer(),164 eh_frame_offset + 4 - fde_record.getCiePointer(),
181 ).?;165 ).?;
182 const eh_frame_sect = object.getSourceSection(object.eh_frame_sect_id.?);166 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, .{
184 .base_addr = eh_frame_sect.addr,168 .base_addr = eh_frame_sect.addr,
185 .base_offset = fde_record_offset,169 .base_offset = fde_record_offset,
186 });170 }) catch continue; // We already handled this error
187 if (source_lsda_ptr) |ptr| {171 if (source_lsda_ptr) |ptr| {
188 const sym_index = object.getSymbolByAddress(ptr, null);172 const sym_index = object.getSymbolByAddress(ptr, null);
189 const sym = object.symtab[sym_index];173 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, .{
191 .base_addr = sect.addr,175 .base_addr = sect.addr,
192 .base_offset = eh_frame_offset,176 .base_offset = eh_frame_offset,
193 });177 }) catch continue; // We already handled this error
194 }178 }
195 },179 },
196 else => unreachable,180 else => unreachable,
...@@ -207,10 +191,10 @@ pub fn write(zld: *Zld, unwind_info: *UnwindInfo) !void {...@@ -207,10 +191,10 @@ pub fn write(zld: *Zld, unwind_info: *UnwindInfo) !void {
207 const cie_record = eh_records.get(191 const cie_record = eh_records.get(
208 eh_frame_offset + 4 - fde_record.getCiePointer(),192 eh_frame_offset + 4 - fde_record.getCiePointer(),
209 ).?;193 ).?;
210 const lsda_ptr = try fde_record.getLsdaPointer(cie_record, .{194 const lsda_ptr = fde_record.getLsdaPointer(cie_record, .{
211 .base_addr = sect.addr,195 .base_addr = sect.addr,
212 .base_offset = eh_frame_offset,196 .base_offset = eh_frame_offset,
213 });197 }) catch continue; // We already handled this error
214 if (lsda_ptr) |ptr| {198 if (lsda_ptr) |ptr| {
215 record.lsda = ptr - seg.vmaddr;199 record.lsda = ptr - seg.vmaddr;
216 }200 }
...@@ -229,7 +213,7 @@ pub fn write(zld: *Zld, unwind_info: *UnwindInfo) !void {...@@ -229,7 +213,7 @@ pub fn write(zld: *Zld, unwind_info: *UnwindInfo) !void {
229 try buffer.appendSlice(record.data);213 try buffer.appendSlice(record.data);
230 }214 }
231215
232 try zld.file.pwriteAll(buffer.items, sect.offset);216 try macho_file.base.file.?.pwriteAll(buffer.items, sect.offset);
233}217}
234const EhFrameRecordTag = enum { cie, fde };218const EhFrameRecordTag = enum { cie, fde };
235219
...@@ -261,12 +245,12 @@ pub fn EhFrameRecord(comptime is_mutable: bool) type {...@@ -261,12 +245,12 @@ pub fn EhFrameRecord(comptime is_mutable: bool) type {
261245
262 pub fn scanRelocs(246 pub fn scanRelocs(
263 rec: Record,247 rec: Record,
264 zld: *Zld,248 macho_file: *MachO,
265 object_id: u32,249 object_id: u32,
266 source_offset: u32,250 source_offset: u32,
267 ) !void {251 ) !void {
268 if (rec.getPersonalityPointerReloc(zld, object_id, source_offset)) |target| {252 if (rec.getPersonalityPointerReloc(macho_file, object_id, source_offset)) |target| {
269 try Atom.addGotEntry(zld, target);253 try macho_file.addGotEntry(target);
270 }254 }
271 }255 }
272256
...@@ -290,12 +274,12 @@ pub fn EhFrameRecord(comptime is_mutable: bool) type {...@@ -290,12 +274,12 @@ pub fn EhFrameRecord(comptime is_mutable: bool) type {
290274
291 pub fn getPersonalityPointerReloc(275 pub fn getPersonalityPointerReloc(
292 rec: Record,276 rec: Record,
293 zld: *Zld,277 macho_file: *MachO,
294 object_id: u32,278 object_id: u32,
295 source_offset: u32,279 source_offset: u32,
296 ) ?SymbolWithLoc {280 ) ?SymbolWithLoc {
297 const cpu_arch = zld.options.target.cpu.arch;281 const cpu_arch = macho_file.base.options.target.cpu.arch;
298 const relocs = getRelocs(zld, object_id, source_offset);282 const relocs = getRelocs(macho_file, object_id, source_offset);
299 for (relocs) |rel| {283 for (relocs) |rel| {
300 switch (cpu_arch) {284 switch (cpu_arch) {
301 .aarch64 => {285 .aarch64 => {
...@@ -317,7 +301,7 @@ pub fn EhFrameRecord(comptime is_mutable: bool) type {...@@ -317,7 +301,7 @@ pub fn EhFrameRecord(comptime is_mutable: bool) type {
317 },301 },
318 else => unreachable,302 else => unreachable,
319 }303 }
320 const target = Atom.parseRelocTarget(zld, .{304 const target = Atom.parseRelocTarget(macho_file, .{
321 .object_id = object_id,305 .object_id = object_id,
322 .rel = rel,306 .rel = rel,
323 .code = rec.data,307 .code = rec.data,
...@@ -328,18 +312,18 @@ pub fn EhFrameRecord(comptime is_mutable: bool) type {...@@ -328,18 +312,18 @@ pub fn EhFrameRecord(comptime is_mutable: bool) type {
328 return null;312 return null;
329 }313 }
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 {
332 source_offset: u32,316 source_offset: u32,
333 out_offset: u32,317 out_offset: u32,
334 sect_addr: u64,318 sect_addr: u64,
335 }) !void {319 }) !void {
336 comptime assert(is_mutable);320 comptime assert(is_mutable);
337321
338 const cpu_arch = zld.options.target.cpu.arch;322 const cpu_arch = macho_file.base.options.target.cpu.arch;
339 const relocs = getRelocs(zld, object_id, ctx.source_offset);323 const relocs = getRelocs(macho_file, object_id, ctx.source_offset);
340324
341 for (relocs) |rel| {325 for (relocs) |rel| {
342 const target = Atom.parseRelocTarget(zld, .{326 const target = Atom.parseRelocTarget(macho_file, .{
343 .object_id = object_id,327 .object_id = object_id,
344 .rel = rel,328 .rel = rel,
345 .code = rec.data,329 .code = rec.data,
...@@ -356,14 +340,14 @@ pub fn EhFrameRecord(comptime is_mutable: bool) type {...@@ -356,14 +340,14 @@ pub fn EhFrameRecord(comptime is_mutable: bool) type {
356 // Address of the __eh_frame in the source object file340 // Address of the __eh_frame in the source object file
357 },341 },
358 .ARM64_RELOC_POINTER_TO_GOT => {342 .ARM64_RELOC_POINTER_TO_GOT => {
359 const target_addr = try Atom.getRelocTargetAddress(zld, target, true, false);343 const target_addr = macho_file.getGotEntryAddress(target).?;
360 const result = math.cast(i32, @as(i64, @intCast(target_addr)) - @as(i64, @intCast(source_addr))) orelse344 const result = math.cast(i32, @as(i64, @intCast(target_addr)) - @as(i64, @intCast(source_addr))) orelse
361 return error.Overflow;345 return error.Overflow;
362 mem.writeIntLittle(i32, rec.data[rel_offset..][0..4], result);346 mem.writeIntLittle(i32, rec.data[rel_offset..][0..4], result);
363 },347 },
364 .ARM64_RELOC_UNSIGNED => {348 .ARM64_RELOC_UNSIGNED => {
365 assert(rel.r_extern == 1);349 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);
367 const result = @as(i64, @intCast(target_addr)) - @as(i64, @intCast(source_addr));351 const result = @as(i64, @intCast(target_addr)) - @as(i64, @intCast(source_addr));
368 mem.writeIntLittle(i64, rec.data[rel_offset..][0..8], @as(i64, @intCast(result)));352 mem.writeIntLittle(i64, rec.data[rel_offset..][0..8], @as(i64, @intCast(result)));
369 },353 },
...@@ -374,7 +358,7 @@ pub fn EhFrameRecord(comptime is_mutable: bool) type {...@@ -374,7 +358,7 @@ pub fn EhFrameRecord(comptime is_mutable: bool) type {
374 const rel_type = @as(macho.reloc_type_x86_64, @enumFromInt(rel.r_type));358 const rel_type = @as(macho.reloc_type_x86_64, @enumFromInt(rel.r_type));
375 switch (rel_type) {359 switch (rel_type) {
376 .X86_64_RELOC_GOT => {360 .X86_64_RELOC_GOT => {
377 const target_addr = try Atom.getRelocTargetAddress(zld, target, true, false);361 const target_addr = macho_file.getGotEntryAddress(target).?;
378 const addend = mem.readIntLittle(i32, rec.data[rel_offset..][0..4]);362 const addend = mem.readIntLittle(i32, rec.data[rel_offset..][0..4]);
379 const adjusted_target_addr = @as(u64, @intCast(@as(i64, @intCast(target_addr)) + addend));363 const adjusted_target_addr = @as(u64, @intCast(@as(i64, @intCast(target_addr)) + addend));
380 const disp = try Relocation.calcPcRelativeDisplacementX86(source_addr, adjusted_target_addr, 0);364 const disp = try Relocation.calcPcRelativeDisplacementX86(source_addr, adjusted_target_addr, 0);
...@@ -388,20 +372,20 @@ pub fn EhFrameRecord(comptime is_mutable: bool) type {...@@ -388,20 +372,20 @@ pub fn EhFrameRecord(comptime is_mutable: bool) type {
388 }372 }
389 }373 }
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 {
392 assert(rec.tag == .fde);376 assert(rec.tag == .fde);
393 const cpu_arch = zld.options.target.cpu.arch;377 const cpu_arch = macho_file.base.options.target.cpu.arch;
394 const addend = mem.readIntLittle(u32, rec.data[0..4]);378 const addend = mem.readIntLittle(u32, rec.data[0..4]);
395 switch (cpu_arch) {379 switch (cpu_arch) {
396 .aarch64 => {380 .aarch64 => {
397 const relocs = getRelocs(zld, object_id, offset);381 const relocs = getRelocs(macho_file, object_id, offset);
398 const maybe_rel = for (relocs) |rel| {382 const maybe_rel = for (relocs) |rel| {
399 if (rel.r_address - @as(i32, @intCast(offset)) == 4 and383 if (rel.r_address - @as(i32, @intCast(offset)) == 4 and
400 @as(macho.reloc_type_arm64, @enumFromInt(rel.r_type)) == .ARM64_RELOC_SUBTRACTOR)384 @as(macho.reloc_type_arm64, @enumFromInt(rel.r_type)) == .ARM64_RELOC_SUBTRACTOR)
401 break rel;385 break rel;
402 } else null;386 } else null;
403 const rel = maybe_rel orelse return addend;387 const rel = maybe_rel orelse return addend;
404 const object = &zld.objects.items[object_id];388 const object = &macho_file.objects.items[object_id];
405 const target_addr = object.in_symtab.?[rel.r_symbolnum].n_value;389 const target_addr = object.in_symtab.?[rel.r_symbolnum].n_value;
406 const sect = object.getSourceSection(object.eh_frame_sect_id.?);390 const sect = object.getSourceSection(object.eh_frame_sect_id.?);
407 return @intCast(sect.addr + offset - target_addr + addend);391 return @intCast(sect.addr + offset - target_addr + addend);
...@@ -583,8 +567,8 @@ pub fn EhFrameRecord(comptime is_mutable: bool) type {...@@ -583,8 +567,8 @@ pub fn EhFrameRecord(comptime is_mutable: bool) type {
583 };567 };
584}568}
585569
586pub fn getRelocs(zld: *Zld, object_id: u32, source_offset: u32) []const macho.relocation_info {570pub fn getRelocs(macho_file: *MachO, object_id: u32, source_offset: u32) []const macho.relocation_info {
587 const object = &zld.objects.items[object_id];571 const object = &macho_file.objects.items[object_id];
588 assert(object.hasEhFrameRecords());572 assert(object.hasEhFrameRecords());
589 const urel = object.eh_frame_relocs_lookup.get(source_offset) orelse573 const urel = object.eh_frame_relocs_lookup.get(source_offset) orelse
590 return &[0]macho.relocation_info{};574 return &[0]macho.relocation_info{};
...@@ -604,7 +588,7 @@ pub const Iterator = struct {...@@ -604,7 +588,7 @@ pub const Iterator = struct {
604588
605 var size = try reader.readIntLittle(u32);589 var size = try reader.readIntLittle(u32);
606 if (size == 0xFFFFFFFF) {590 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", .{});
608 return error.BadDwarfCfi;592 return error.BadDwarfCfi;
609 }593 }
610594
...@@ -650,3 +634,18 @@ pub const EH_PE = struct {...@@ -650,3 +634,18 @@ pub const EH_PE = struct {
650 pub const indirect = 0x80;634 pub const indirect = 0x80;
651 pub const omit = 0xFF;635 pub const omit = 0xFF;
652};636};
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 @@...@@ -1,42 +1,44 @@
1const std = @import("std");1pub fn isFatLibrary(file: std.fs.File) bool {
2const log = std.log.scoped(.archive);2 const reader = file.reader();
3const macho = std.macho;3 const hdr = reader.readStructBig(macho.fat_header) catch return false;
4const mem = std.mem;4 defer file.seekTo(0) catch {};
55 return hdr.magic == macho.FAT_MAGIC;
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;
18}6}
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();
21 const fat_header = try reader.readStructBig(macho.fat_header);16 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
24 var fat_arch_index: u32 = 0;22 var fat_arch_index: u32 = 0;
25 while (fat_arch_index < fat_header.nfat_arch) : (fat_arch_index += 1) {23 while (fat_arch_index < fat_header.nfat_arch) : (fat_arch_index += 1) {
26 const fat_arch = try reader.readStructBig(macho.fat_arch);24 const fat_arch = try reader.readStructBig(macho.fat_arch);
27 // If we come across an architecture that we do not know how to handle, that's25 // If we come across an architecture that we do not know how to handle, that's
28 // fine because we can keep looking for one that might match.26 // fine because we can keep looking for one that might match.
29 const lib_arch = decodeArch(fat_arch.cputype, false) catch |err| switch (err) {27 const arch: std.Target.Cpu.Arch = switch (fat_arch.cputype) {
30 error.UnsupportedCpuArchitecture => continue,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,
31 };31 };
32 if (lib_arch == cpu_arch) {32
33 // We have found a matching architecture!33 archs.appendAssumeCapacity(.{ .tag = arch, .offset = fat_arch.offset });
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;
41 }34 }
35
36 return archs.toOwnedSlice();
42}37}
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 @@...@@ -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
10pub fn ParallelHasher(comptime Hasher: type) type {1pub fn ParallelHasher(comptime Hasher: type) type {
11 const hash_size = Hasher.digest_length;2 const hash_size = Hasher.digest_length;
123
...@@ -69,3 +60,12 @@ pub fn ParallelHasher(comptime Hasher: type) type {...@@ -69,3 +60,12 @@ pub fn ParallelHasher(comptime Hasher: type) type {
69 const Self = @This();60 const Self = @This();
70 };61 };
71}62}
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 @@...@@ -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
11/// Default implicit entrypoint symbol name.1/// Default implicit entrypoint symbol name.
12pub const default_entry_point: []const u8 = "_main";2pub const default_entry_point: []const u8 = "_main";
133
...@@ -86,8 +76,14 @@ fn calcLCsSize(gpa: Allocator, options: *const link.Options, ctx: CalcLCsSizeCtx...@@ -86,8 +76,14 @@ fn calcLCsSize(gpa: Allocator, options: *const link.Options, ctx: CalcLCsSizeCtx
86 }76 }
87 // LC_SOURCE_VERSION77 // LC_SOURCE_VERSION
88 sizeofcmds += @sizeOf(macho.source_version_command);78 sizeofcmds += @sizeOf(macho.source_version_command);
89 // LC_BUILD_VERSION79 // LC_BUILD_VERSION or LC_VERSION_MIN_
90 sizeofcmds += @sizeOf(macho.build_version_command) + @sizeOf(macho.build_tool_version);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 }
91 // LC_UUID87 // LC_UUID
92 sizeofcmds += @sizeOf(macho.uuid_command);88 sizeofcmds += @sizeOf(macho.uuid_command);
93 // LC_LOAD_DYLIB89 // LC_LOAD_DYLIB
...@@ -101,17 +97,8 @@ fn calcLCsSize(gpa: Allocator, options: *const link.Options, ctx: CalcLCsSizeCtx...@@ -101,17 +97,8 @@ fn calcLCsSize(gpa: Allocator, options: *const link.Options, ctx: CalcLCsSizeCtx
101 );97 );
102 }98 }
103 // LC_CODE_SIGNATURE99 // LC_CODE_SIGNATURE
104 {100 if (MachO.requiresCodeSignature(options)) {
105 const target = options.target;101 sizeofcmds += @sizeOf(macho.linkedit_data_command);
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 }
115 }102 }
116103
117 return @as(u32, @intCast(sizeofcmds));104 return @as(u32, @intCast(sizeofcmds));
...@@ -271,33 +258,28 @@ pub fn writeRpathLCs(gpa: Allocator, options: *const link.Options, lc_writer: an...@@ -271,33 +258,28 @@ pub fn writeRpathLCs(gpa: Allocator, options: *const link.Options, lc_writer: an
271 }258 }
272}259}
273260
274pub fn writeBuildVersionLC(options: *const link.Options, lc_writer: anytype) !void {261pub fn writeVersionMinLC(platform: Platform, sdk_version: ?std.SemanticVersion, lc_writer: anytype) !void {
275 const cmdsize = @sizeOf(macho.build_version_command) + @sizeOf(macho.build_tool_version);262 const cmd: macho.LC = switch (platform.os_tag) {
276 const platform_version = blk: {263 .macos => .VERSION_MIN_MACOSX,
277 const ver = options.target.os.version_range.semver.min;264 .ios => .VERSION_MIN_IPHONEOS,
278 const platform_version = @as(u32, @intCast(ver.major << 16 | ver.minor << 8));265 .tvos => .VERSION_MIN_TVOS,
279 break :blk platform_version;266 .watchos => .VERSION_MIN_WATCHOS,
267 else => unreachable,
280 };268 };
281 const sdk_version: ?std.SemanticVersion = options.darwin_sdk_version orelse blk: {269 try lc_writer.writeAll(mem.asBytes(&macho.version_min_command{
282 if (options.sysroot) |path| break :blk inferSdkVersionFromSdkPath(path);270 .cmd = cmd,
283 break :blk null;271 .version = platform.toAppleVersion(),
284 };272 .sdk = if (sdk_version) |ver| semanticVersionToAppleVersion(ver) else platform.toAppleVersion(),
285 const sdk_version_value: u32 = if (sdk_version) |ver|273 }));
286 @intCast(ver.major << 16 | ver.minor << 8)274}
287 else275
288 platform_version;276pub fn writeBuildVersionLC(platform: Platform, sdk_version: ?std.SemanticVersion, lc_writer: anytype) !void {
289 const is_simulator_abi = options.target.abi == .simulator;277 const cmdsize = @sizeOf(macho.build_version_command) + @sizeOf(macho.build_tool_version);
290 try lc_writer.writeStruct(macho.build_version_command{278 try lc_writer.writeStruct(macho.build_version_command{
291 .cmdsize = cmdsize,279 .cmdsize = cmdsize,
292 .platform = switch (options.target.os.tag) {280 .platform = platform.toApplePlatform(),
293 .macos => .MACOS,281 .minos = platform.toAppleVersion(),
294 .ios => if (is_simulator_abi) macho.PLATFORM.IOSSIMULATOR else macho.PLATFORM.IOS,282 .sdk = if (sdk_version) |ver| semanticVersionToAppleVersion(ver) else platform.toAppleVersion(),
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,
301 .ntools = 1,283 .ntools = 1,
302 });284 });
303 try lc_writer.writeAll(mem.asBytes(&macho.build_tool_version{285 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...@@ -320,7 +302,160 @@ pub fn writeLoadDylibLCs(dylibs: []const Dylib, referenced: []u16, lc_writer: an
320 }302 }
321}303}
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 {
324 const stem = std.fs.path.stem(path);459 const stem = std.fs.path.stem(path);
325 const start = for (stem, 0..) |c, i| {460 const start = for (stem, 0..) |c, i| {
326 if (std.ascii.isDigit(c)) break i;461 if (std.ascii.isDigit(c)) break i;
...@@ -374,3 +509,14 @@ test "parseSdkVersion" {...@@ -374,3 +509,14 @@ test "parseSdkVersion" {
374509
375 try expect(parseSdkVersion("11") == null);510 try expect(parseSdkVersion("11") == null);
376}511}
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 @@...@@ -1,9 +1,4 @@
1const std = @import("std");1pub inline fn stubHelperPreambleSize(cpu_arch: std.Target.Cpu.Arch) u8 {
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 {
7 return switch (cpu_arch) {2 return switch (cpu_arch) {
8 .x86_64 => 15,3 .x86_64 => 15,
9 .aarch64 => 6 * @sizeOf(u32),4 .aarch64 => 6 * @sizeOf(u32),
...@@ -11,7 +6,7 @@ pub inline fn calcStubHelperPreambleSize(cpu_arch: std.Target.Cpu.Arch) u5 {...@@ -11,7 +6,7 @@ pub inline fn calcStubHelperPreambleSize(cpu_arch: std.Target.Cpu.Arch) u5 {
11 };6 };
12}7}
138
14pub inline fn calcStubHelperEntrySize(cpu_arch: std.Target.Cpu.Arch) u4 {9pub inline fn stubHelperSize(cpu_arch: std.Target.Cpu.Arch) u8 {
15 return switch (cpu_arch) {10 return switch (cpu_arch) {
16 .x86_64 => 10,11 .x86_64 => 10,
17 .aarch64 => 3 * @sizeOf(u32),12 .aarch64 => 3 * @sizeOf(u32),
...@@ -19,7 +14,7 @@ pub inline fn calcStubHelperEntrySize(cpu_arch: std.Target.Cpu.Arch) u4 {...@@ -19,7 +14,7 @@ pub inline fn calcStubHelperEntrySize(cpu_arch: std.Target.Cpu.Arch) u4 {
19 };14 };
20}15}
2116
22pub inline fn calcStubEntrySize(cpu_arch: std.Target.Cpu.Arch) u4 {17pub inline fn stubSize(cpu_arch: std.Target.Cpu.Arch) u8 {
23 return switch (cpu_arch) {18 return switch (cpu_arch) {
24 .x86_64 => 6,19 .x86_64 => 6,
25 .aarch64 => 3 * @sizeOf(u32),20 .aarch64 => 3 * @sizeOf(u32),
...@@ -27,7 +22,15 @@ pub inline fn calcStubEntrySize(cpu_arch: std.Target.Cpu.Arch) u4 {...@@ -27,7 +22,15 @@ pub inline fn calcStubEntrySize(cpu_arch: std.Target.Cpu.Arch) u4 {
27 };22 };
28}23}
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 {
31 return switch (cpu_arch) {34 return switch (cpu_arch) {
32 .x86_64 => 1,35 .x86_64 => 1,
33 .aarch64 => 2 * @sizeOf(u32),36 .aarch64 => 2 * @sizeOf(u32),
...@@ -159,3 +162,8 @@ pub fn writeStubCode(args: struct {...@@ -159,3 +162,8 @@ pub fn writeStubCode(args: struct {
159 else => unreachable,162 else => unreachable,
160 }163 }
161}164}
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 @@...@@ -5,24 +5,6 @@
5//! The algorithm works pessimistically and assumes that any reference to an Atom in5//! The algorithm works pessimistically and assumes that any reference to an Atom in
6//! another output section is out of range.6//! 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
26/// Branch instruction has 26 bits immediate but 4 byte aligned.8/// Branch instruction has 26 bits immediate but 4 byte aligned.
27const jump_bits = @bitSizeOf(i28);9const jump_bits = @bitSizeOf(i28);
2810
...@@ -35,21 +17,35 @@ const max_distance = (1 << (jump_bits - 1));...@@ -35,21 +17,35 @@ const max_distance = (1 << (jump_bits - 1));
35const max_allowed_distance = max_distance - 0x500_000;17const max_allowed_distance = max_distance - 0x500_000;
3618
37pub const Thunk = struct {19pub const Thunk = struct {
38 start_index: AtomIndex,20 start_index: Atom.Index,
39 len: u32,21 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
43 pub fn deinit(self: *Thunk, gpa: Allocator) void {38 pub fn deinit(self: *Thunk, gpa: Allocator) void {
39 self.targets.deinit(gpa);
44 self.lookup.deinit(gpa);40 self.lookup.deinit(gpa);
45 }41 }
4642
47 pub fn getStartAtomIndex(self: Thunk) AtomIndex {43 pub fn getStartAtomIndex(self: Thunk) Atom.Index {
48 assert(self.len != 0);44 assert(self.len != 0);
49 return self.start_index;45 return self.start_index;
50 }46 }
5147
52 pub fn getEndAtomIndex(self: Thunk) AtomIndex {48 pub fn getEndAtomIndex(self: Thunk) Atom.Index {
53 assert(self.len != 0);49 assert(self.len != 0);
54 return self.start_index + self.len - 1;50 return self.start_index + self.len - 1;
55 }51 }
...@@ -62,19 +58,18 @@ pub const Thunk = struct {...@@ -62,19 +58,18 @@ pub const Thunk = struct {
62 return @alignOf(u32);58 return @alignOf(u32);
63 }59 }
6460
65 pub fn getTrampolineForSymbol(self: Thunk, zld: *Zld, target: SymbolWithLoc) ?SymbolWithLoc {61 pub fn getTrampoline(self: Thunk, macho_file: *MachO, tag: Tag, target: SymbolWithLoc) ?SymbolWithLoc {
66 const atom_index = self.lookup.get(target) orelse return null;62 const atom_index = self.lookup.get(.{ .tag = tag, .target = target }) orelse return null;
67 const atom = zld.getAtom(atom_index);63 return macho_file.getAtom(atom_index).getSymbolWithLoc();
68 return atom.getSymbolWithLoc();
69 }64 }
70};65};
7166
72pub fn createThunks(zld: *Zld, sect_id: u8) !void {67pub fn createThunks(macho_file: *MachO, sect_id: u8) !void {
73 const header = &zld.sections.items(.header)[sect_id];68 const header = &macho_file.sections.items(.header)[sect_id];
74 if (header.size == 0) return;69 if (header.size == 0) return;
7570
76 const gpa = zld.gpa;71 const gpa = macho_file.base.allocator;
77 const first_atom_index = zld.sections.items(.first_atom_index)[sect_id];72 const first_atom_index = macho_file.sections.items(.first_atom_index)[sect_id].?;
7873
79 header.size = 0;74 header.size = 0;
80 header.@"align" = 0;75 header.@"align" = 0;
...@@ -84,8 +79,8 @@ pub fn createThunks(zld: *Zld, sect_id: u8) !void {...@@ -84,8 +79,8 @@ pub fn createThunks(zld: *Zld, sect_id: u8) !void {
84 {79 {
85 var atom_index = first_atom_index;80 var atom_index = first_atom_index;
86 while (true) {81 while (true) {
87 const atom = zld.getAtom(atom_index);82 const atom = macho_file.getAtom(atom_index);
88 const sym = zld.getSymbolPtr(atom.getSymbolWithLoc());83 const sym = macho_file.getSymbolPtr(atom.getSymbolWithLoc());
89 sym.n_value = 0;84 sym.n_value = 0;
90 atom_count += 1;85 atom_count += 1;
9186
...@@ -95,7 +90,7 @@ pub fn createThunks(zld: *Zld, sect_id: u8) !void {...@@ -95,7 +90,7 @@ pub fn createThunks(zld: *Zld, sect_id: u8) !void {
95 }90 }
96 }91 }
9792
98 var allocated = std.AutoHashMap(AtomIndex, void).init(gpa);93 var allocated = std.AutoHashMap(Atom.Index, void).init(gpa);
99 defer allocated.deinit();94 defer allocated.deinit();
100 try allocated.ensureTotalCapacity(atom_count);95 try allocated.ensureTotalCapacity(atom_count);
10196
...@@ -104,24 +99,24 @@ pub fn createThunks(zld: *Zld, sect_id: u8) !void {...@@ -104,24 +99,24 @@ pub fn createThunks(zld: *Zld, sect_id: u8) !void {
104 var offset: u64 = 0;99 var offset: u64 = 0;
105100
106 while (true) {101 while (true) {
107 const group_start_atom = zld.getAtom(group_start);102 const group_start_atom = macho_file.getAtom(group_start);
108 log.debug("GROUP START at {d}", .{group_start});103 log.debug("GROUP START at {d}", .{group_start});
109104
110 while (true) {105 while (true) {
111 const atom = zld.getAtom(group_end);106 const atom = macho_file.getAtom(group_end);
112 offset = mem.alignForward(u64, offset, try math.powi(u32, 2, atom.alignment));107 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());
115 sym.n_value = offset;110 sym.n_value = offset;
116 offset += atom.size;111 offset += atom.size;
117112
118 zld.logAtom(group_end, log);113 macho_file.logAtom(group_end, log);
119114
120 header.@"align" = @max(header.@"align", atom.alignment);115 header.@"align" = @max(header.@"align", atom.alignment);
121116
122 allocated.putAssumeCapacityNoClobber(group_end, {});117 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());
125 if (offset - group_start_sym.n_value >= max_allowed_distance) break;120 if (offset - group_start_sym.n_value >= max_allowed_distance) break;
126121
127 if (atom.next_index) |next_index| {122 if (atom.next_index) |next_index| {
...@@ -131,15 +126,15 @@ pub fn createThunks(zld: *Zld, sect_id: u8) !void {...@@ -131,15 +126,15 @@ pub fn createThunks(zld: *Zld, sect_id: u8) !void {
131 log.debug("GROUP END at {d}", .{group_end});126 log.debug("GROUP END at {d}", .{group_end});
132127
133 // Insert thunk at group_end128 // Insert thunk at group_end
134 const thunk_index = @as(u32, @intCast(zld.thunks.items.len));129 const thunk_index = @as(u32, @intCast(macho_file.thunks.items.len));
135 try zld.thunks.append(gpa, .{ .start_index = undefined, .len = 0 });130 try macho_file.thunks.append(gpa, .{ .start_index = undefined, .len = 0 });
136131
137 // Scan relocs in the group and create trampolines for any unreachable callsite.132 // Scan relocs in the group and create trampolines for any unreachable callsite.
138 var atom_index = group_start;133 var atom_index = group_start;
139 while (true) {134 while (true) {
140 const atom = zld.getAtom(atom_index);135 const atom = macho_file.getAtom(atom_index);
141 try scanRelocs(136 try scanRelocs(
142 zld,137 macho_file,
143 atom_index,138 atom_index,
144 allocated,139 allocated,
145 thunk_index,140 thunk_index,
...@@ -154,19 +149,19 @@ pub fn createThunks(zld: *Zld, sect_id: u8) !void {...@@ -154,19 +149,19 @@ pub fn createThunks(zld: *Zld, sect_id: u8) !void {
154 }149 }
155150
156 offset = mem.alignForward(u64, offset, Thunk.getAlignment());151 offset = mem.alignForward(u64, offset, Thunk.getAlignment());
157 allocateThunk(zld, thunk_index, offset, header);152 allocateThunk(macho_file, thunk_index, offset, header);
158 offset += zld.thunks.items[thunk_index].getSize();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];
161 if (thunk.len == 0) {156 if (thunk.len == 0) {
162 const group_end_atom = zld.getAtom(group_end);157 const group_end_atom = macho_file.getAtom(group_end);
163 if (group_end_atom.next_index) |next_index| {158 if (group_end_atom.next_index) |next_index| {
164 group_start = next_index;159 group_start = next_index;
165 group_end = next_index;160 group_end = next_index;
166 } else break;161 } else break;
167 } else {162 } else {
168 const thunk_end_atom_index = thunk.getEndAtomIndex();163 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);
170 if (thunk_end_atom.next_index) |next_index| {165 if (thunk_end_atom.next_index) |next_index| {
171 group_start = next_index;166 group_start = next_index;
172 group_end = next_index;167 group_end = next_index;
...@@ -178,12 +173,12 @@ pub fn createThunks(zld: *Zld, sect_id: u8) !void {...@@ -178,12 +173,12 @@ pub fn createThunks(zld: *Zld, sect_id: u8) !void {
178}173}
179174
180fn allocateThunk(175fn allocateThunk(
181 zld: *Zld,176 macho_file: *MachO,
182 thunk_index: ThunkIndex,177 thunk_index: Thunk.Index,
183 base_offset: u64,178 base_offset: u64,
184 header: *macho.section_64,179 header: *macho.section_64,
185) void {180) void {
186 const thunk = zld.thunks.items[thunk_index];181 const thunk = macho_file.thunks.items[thunk_index];
187 if (thunk.len == 0) return;182 if (thunk.len == 0) return;
188183
189 const first_atom_index = thunk.getStartAtomIndex();184 const first_atom_index = thunk.getStartAtomIndex();
...@@ -192,14 +187,14 @@ fn allocateThunk(...@@ -192,14 +187,14 @@ fn allocateThunk(
192 var atom_index = first_atom_index;187 var atom_index = first_atom_index;
193 var offset = base_offset;188 var offset = base_offset;
194 while (true) {189 while (true) {
195 const atom = zld.getAtom(atom_index);190 const atom = macho_file.getAtom(atom_index);
196 offset = mem.alignForward(u64, offset, Thunk.getAlignment());191 offset = mem.alignForward(u64, offset, Thunk.getAlignment());
197192
198 const sym = zld.getSymbolPtr(atom.getSymbolWithLoc());193 const sym = macho_file.getSymbolPtr(atom.getSymbolWithLoc());
199 sym.n_value = offset;194 sym.n_value = offset;
200 offset += atom.size;195 offset += atom.size;
201196
202 zld.logAtom(atom_index, log);197 macho_file.logAtom(atom_index, log);
203198
204 header.@"align" = @max(header.@"align", atom.alignment);199 header.@"align" = @max(header.@"align", atom.alignment);
205200
...@@ -212,80 +207,83 @@ fn allocateThunk(...@@ -212,80 +207,83 @@ fn allocateThunk(
212}207}
213208
214fn scanRelocs(209fn scanRelocs(
215 zld: *Zld,210 macho_file: *MachO,
216 atom_index: AtomIndex,211 atom_index: Atom.Index,
217 allocated: std.AutoHashMap(AtomIndex, void),212 allocated: std.AutoHashMap(Atom.Index, void),
218 thunk_index: ThunkIndex,213 thunk_index: Thunk.Index,
219 group_end: AtomIndex,214 group_end: Atom.Index,
220) !void {215) !void {
221 const atom = zld.getAtom(atom_index);216 const atom = macho_file.getAtom(atom_index);
222 const object = zld.objects.items[atom.getFile().?];217 const object = macho_file.objects.items[atom.getFile().?];
223218
224 const base_offset = if (object.getSourceSymbol(atom.sym_index)) |source_sym| blk: {219 const base_offset = if (object.getSourceSymbol(atom.sym_index)) |source_sym| blk: {
225 const source_sect = object.getSourceSection(source_sym.n_sect - 1);220 const source_sect = object.getSourceSection(source_sym.n_sect - 1);
226 break :blk @as(i32, @intCast(source_sym.n_value - source_sect.addr));221 break :blk @as(i32, @intCast(source_sym.n_value - source_sect.addr));
227 } else 0;222 } else 0;
228223
229 const code = Atom.getAtomCode(zld, atom_index);224 const code = Atom.getAtomCode(macho_file, atom_index);
230 const relocs = Atom.getAtomRelocs(zld, atom_index);225 const relocs = Atom.getAtomRelocs(macho_file, atom_index);
231 const ctx = Atom.getRelocContext(zld, atom_index);226 const ctx = Atom.getRelocContext(macho_file, atom_index);
232227
233 for (relocs) |rel| {228 for (relocs) |rel| {
234 if (!relocNeedsThunk(rel)) continue;229 if (!relocNeedsThunk(rel)) continue;
235230
236 const target = Atom.parseRelocTarget(zld, .{231 const target = Atom.parseRelocTarget(macho_file, .{
237 .object_id = atom.getFile().?,232 .object_id = atom.getFile().?,
238 .rel = rel,233 .rel = rel,
239 .code = code,234 .code = code,
240 .base_offset = ctx.base_offset,235 .base_offset = ctx.base_offset,
241 .base_addr = ctx.base_addr,236 .base_addr = ctx.base_addr,
242 });237 });
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
245 log.debug("{x}: source = {s}@{x}, target = {s}@{x} unreachable", .{240 log.debug("{x}: source = {s}@{x}, target = {s}@{x} unreachable", .{
246 rel.r_address - base_offset,241 rel.r_address - base_offset,
247 zld.getSymbolName(atom.getSymbolWithLoc()),242 macho_file.getSymbolName(atom.getSymbolWithLoc()),
248 zld.getSymbol(atom.getSymbolWithLoc()).n_value,243 macho_file.getSymbol(atom.getSymbolWithLoc()).n_value,
249 zld.getSymbolName(target),244 macho_file.getSymbolName(target),
250 zld.getSymbol(target).n_value,245 macho_file.getSymbol(target).n_value,
251 });246 });
252247
253 const gpa = zld.gpa;248 const gpa = macho_file.base.allocator;
254 const target_sym = zld.getSymbol(target);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: {252 const tag: Thunk.Tag = if (target_sym.undf()) .stub else .atom;
257 const stub_atom_index = zld.getStubsAtomIndexForSymbol(target).?;253 const thunk_target: Thunk.Target = .{ .tag = tag, .target = target };
258 break :blk .{ .sym_index = zld.getAtom(stub_atom_index).sym_index };254 const gop = try thunk.lookup.getOrPut(gpa, thunk_target);
259 } else target;
260
261 const thunk = &zld.thunks.items[thunk_index];
262 const gop = try thunk.lookup.getOrPut(gpa, actual_target);
263 if (!gop.found_existing) {255 if (!gop.found_existing) {
264 const thunk_atom_index = try createThunkAtom(zld);256 gop.value_ptr.* = try pushThunkAtom(macho_file, thunk, group_end);
265 gop.value_ptr.* = thunk_atom_index;257 try thunk.targets.append(gpa, thunk_target);
258 }
266259
267 const thunk_atom = zld.getAtomPtr(thunk_atom_index);260 try macho_file.thunk_table.put(gpa, atom_index, thunk_index);
268 const end_atom_index = if (thunk.len == 0) group_end else thunk.getEndAtomIndex();261 }
269 const end_atom = zld.getAtomPtr(end_atom_index);262}
270263
271 if (end_atom.next_index) |first_after_index| {264fn pushThunkAtom(macho_file: *MachO, thunk: *Thunk, group_end: Atom.Index) !Atom.Index {
272 const first_after_atom = zld.getAtomPtr(first_after_index);265 const thunk_atom_index = try createThunkAtom(macho_file);
273 first_after_atom.prev_index = thunk_atom_index;
274 thunk_atom.next_index = first_after_index;
275 }
276266
277 end_atom.next_index = thunk_atom_index;267 const thunk_atom = macho_file.getAtomPtr(thunk_atom_index);
278 thunk_atom.prev_index = end_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) {271 if (end_atom.next_index) |first_after_index| {
281 thunk.start_index = thunk_atom_index;272 const first_after_atom = macho_file.getAtomPtr(first_after_index);
282 }273 first_after_atom.prev_index = thunk_atom_index;
274 thunk_atom.next_index = first_after_index;
275 }
283276
284 thunk.len += 1;277 end_atom.next_index = thunk_atom_index;
285 }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;
288 }282 }
283
284 thunk.len += 1;
285
286 return thunk_atom_index;
289}287}
290288
291inline fn relocNeedsThunk(rel: macho.relocation_info) bool {289inline fn relocNeedsThunk(rel: macho.relocation_info) bool {
...@@ -294,80 +292,78 @@ inline fn relocNeedsThunk(rel: macho.relocation_info) bool {...@@ -294,80 +292,78 @@ inline fn relocNeedsThunk(rel: macho.relocation_info) bool {
294}292}
295293
296fn isReachable(294fn isReachable(
297 zld: *Zld,295 macho_file: *MachO,
298 atom_index: AtomIndex,296 atom_index: Atom.Index,
299 rel: macho.relocation_info,297 rel: macho.relocation_info,
300 base_offset: i32,298 base_offset: i32,
301 target: SymbolWithLoc,299 target: SymbolWithLoc,
302 allocated: std.AutoHashMap(AtomIndex, void),300 allocated: std.AutoHashMap(Atom.Index, void),
303) bool {301) 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);304 const source_atom = macho_file.getAtom(atom_index);
307 const source_sym = zld.getSymbol(source_atom.getSymbolWithLoc());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().?];
310 const target_atom_index = target_object.getAtomIndexForSymbol(target.sym_index).?;308 const target_atom_index = target_object.getAtomIndexForSymbol(target.sym_index).?;
311 const target_atom = zld.getAtom(target_atom_index);309 const target_atom = macho_file.getAtom(target_atom_index);
312 const target_sym = zld.getSymbol(target_atom.getSymbolWithLoc());310 const target_sym = macho_file.getSymbol(target_atom.getSymbolWithLoc());
313311
314 if (source_sym.n_sect != target_sym.n_sect) return false;312 if (source_sym.n_sect != target_sym.n_sect) return false;
315313
316 if (!allocated.contains(target_atom_index)) return false;314 if (!allocated.contains(target_atom_index)) return false;
317315
318 const source_addr = source_sym.n_value + @as(u32, @intCast(rel.r_address - base_offset));316 const source_addr = source_sym.n_value + @as(u32, @intCast(rel.r_address - base_offset));
319 const is_via_got = Atom.relocRequiresGot(zld, rel);317 const target_addr = if (Atom.relocRequiresGot(macho_file, rel))
320 const target_addr = Atom.getRelocTargetAddress(zld, target, is_via_got, false) catch unreachable;318 macho_file.getGotEntryAddress(target).?
319 else
320 Atom.getRelocTargetAddress(macho_file, target, false);
321 _ = Relocation.calcPcRelativeDisplacementArm64(source_addr, target_addr) catch321 _ = Relocation.calcPcRelativeDisplacementArm64(source_addr, target_addr) catch
322 return false;322 return false;
323323
324 return true;324 return true;
325}325}
326326
327fn createThunkAtom(zld: *Zld) !AtomIndex {327fn createThunkAtom(macho_file: *MachO) !Atom.Index {
328 const sym_index = try zld.allocateSymbol();328 const sym_index = try macho_file.allocateSymbol();
329 const atom_index = try zld.createEmptyAtom(sym_index, @sizeOf(u32) * 3, 2);329 const atom_index = try macho_file.createAtom(sym_index, .{ .size = @sizeOf(u32) * 3, .alignment = 2 });
330 const sym = zld.getSymbolPtr(.{ .sym_index = sym_index });330 const sym = macho_file.getSymbolPtr(.{ .sym_index = sym_index });
331 sym.n_type = macho.N_SECT;331 sym.n_type = macho.N_SECT;
332332 sym.n_sect = macho_file.text_section_index.? + 1;
333 const sect_id = zld.getSectionByName("__TEXT", "__text") orelse unreachable;
334 sym.n_sect = sect_id + 1;
335
336 return atom_index;333 return atom_index;
337}334}
338335
339fn getThunkIndex(zld: *Zld, atom_index: AtomIndex) ?ThunkIndex {336pub fn writeThunkCode(macho_file: *MachO, thunk: *const Thunk, writer: anytype) !void {
340 const atom = zld.getAtom(atom_index);337 const slice = thunk.targets.slice();
341 const sym = zld.getSymbol(atom.getSymbolWithLoc());338 for (thunk.getStartAtomIndex()..thunk.getEndAtomIndex(), 0..) |atom_index, target_index| {
342 for (zld.thunks.items, 0..) |thunk, i| {339 const atom = macho_file.getAtom(@intCast(atom_index));
343 if (thunk.len == 0) continue;340 const sym = macho_file.getSymbol(atom.getSymbolWithLoc());
344341 const source_addr = sym.n_value;
345 const thunk_atom_index = thunk.getStartAtomIndex();342 const tag = slice.items(.tag)[target_index];
346 const thunk_atom = zld.getAtom(thunk_atom_index);343 const target = slice.items(.target)[target_index];
347 const thunk_sym = zld.getSymbol(thunk_atom.getSymbolWithLoc());344 const target_addr = switch (tag) {
348 const start_addr = thunk_sym.n_value;345 .stub => macho_file.getStubsEntryAddress(target).?,
349 const end_addr = start_addr + thunk.getSize();346 .atom => macho_file.getSymbol(target).n_value,
350347 };
351 if (start_addr <= sym.n_value and sym.n_value < end_addr) {348 const pages = Relocation.calcNumberOfPages(source_addr, target_addr);
352 return @as(u32, @intCast(i));349 try writer.writeIntLittle(u32, aarch64.Instruction.adrp(.x16, pages).toU32());
353 }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());
354 }353 }
355 return null;
356}354}
357355
358pub fn writeThunkCode(zld: *Zld, atom_index: AtomIndex, writer: anytype) !void {356const std = @import("std");
359 const atom = zld.getAtom(atom_index);357const assert = std.debug.assert;
360 const sym = zld.getSymbol(atom.getSymbolWithLoc());358const log = std.log.scoped(.thunks);
361 const source_addr = sym.n_value;359const macho = std.macho;
362 const thunk = zld.thunks.items[getThunkIndex(zld, atom_index).?];360const math = std.math;
363 const target_addr = for (thunk.lookup.keys()) |target| {361const mem = std.mem;
364 const target_atom_index = thunk.lookup.get(target).?;362
365 if (atom_index == target_atom_index) break zld.getSymbol(target).n_value;363const aarch64 = @import("../../arch/aarch64/bits.zig");
366 } else unreachable;364
367365const Allocator = mem.Allocator;
368 const pages = Relocation.calcNumberOfPages(source_addr, target_addr);366const Atom = @import("Atom.zig");
369 try writer.writeIntLittle(u32, aarch64.Instruction.adrp(.x16, pages).toU32());367const MachO = @import("../MachO.zig");
370 const off = try Relocation.calcPageOffset(target_addr, .arithmetic);368const Relocation = @import("Relocation.zig");
371 try writer.writeIntLittle(u32, aarch64.Instruction.add(.x16, .x16, off, false).toU32());369const SymbolWithLoc = MachO.SymbolWithLoc;
372 try writer.writeIntLittle(u32, aarch64.Instruction.br(.x16).toU32());
373}
src/link/MachO/uuid.zig+9-9
...@@ -1,12 +1,3 @@...@@ -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
10/// Calculates Md5 hash of each chunk in parallel and then hashes all Md5 hashes to produce1/// Calculates Md5 hash of each chunk in parallel and then hashes all Md5 hashes to produce
11/// the final digest.2/// the final digest.
12/// While this is NOT a correct MD5 hash of the contents, this methodology is used by LLVM/LLD3/// 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 {...@@ -43,3 +34,12 @@ inline fn conform(out: *[Md5.digest_length]u8) void {
43 out[6] = (out[6] & 0x0F) | (3 << 4);34 out[6] = (out[6] & 0x0F) | (3 << 4);
44 out[8] = (out[8] & 0x3F) | 0x80;35 out[8] = (out[8] & 0x3F) | 0x80;
45}36}
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 @@...@@ -1,3940 +1,1231 @@
1const std = @import("std");1pub fn linkWithZld(
2const build_options = @import("build_options");2 macho_file: *MachO,
3const assert = std.debug.assert;3 comp: *Compilation,
4const dwarf = std.dwarf;4 prog_node: *std.Progress.Node,
5const fs = std.fs;5) link.File.FlushError!void {
6const log = std.log.scoped(.link);6 const tracy = trace(@src());
7const macho = std.macho;7 defer tracy.end();
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;
22908
2291 offsets.appendAssumeCapacity(diff);9 const gpa = macho_file.base.allocator;
2292 last_off = offset;10 const options = &macho_file.base.options;
2293 }11 const target = options.target;
229412
2295 var buffer = std.ArrayList(u8).init(gpa);13 var arena_allocator = std.heap.ArenaAllocator.init(gpa);
2296 defer buffer.deinit();14 defer arena_allocator.deinit();
15 const arena = arena_allocator.allocator();
229716
2298 const max_size = @as(usize, @intCast(offsets.items.len * @sizeOf(u64)));17 const directory = options.emit.?.directory; // Just an alias to make it shorter to type.
2299 try buffer.ensureTotalCapacity(max_size);18 const full_out_path = try directory.join(arena, &[_][]const u8{options.emit.?.sub_path});
230019
2301 for (offsets.items) |offset| {20 // If there is no Zig code to compile, then we should skip flushing the output file because it
2302 try std.leb.writeULEB128(buffer.writer(), offset);21 // will not be part of the linker line anyway.
2303 }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();25 if (fs.path.dirname(full_out_path)) |dirname| {
2306 const offset = link_seg.fileoff + link_seg.filesize;26 break :blk try fs.path.join(arena, &.{ dirname, macho_file.base.intermediary_basename.? });
2307 assert(mem.isAlignedGeneric(u64, offset, @alignOf(u64)));27 } else {
2308 const needed_size = buffer.items.len;28 break :blk macho_file.base.intermediary_basename.?;
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);
2314 }29 }
2315 link_seg.filesize = offset + needed_size_aligned - link_seg.fileoff;30 } else null;
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);
232031
2321 self.function_starts_cmd.dataoff = @as(u32, @intCast(offset));32 var sub_prog_node = prog_node.start("MachO Flush", 0);
2322 self.function_starts_cmd.datasize = @as(u32, @intCast(needed_size_aligned));33 sub_prog_node.activate();
2323 }34 sub_prog_node.context.refresh();
35 defer sub_prog_node.end();
232436
2325 fn filterDataInCode(37 const cpu_arch = target.cpu.arch;
2326 dices: []const macho.data_in_code_entry,38 const is_lib = options.output_mode == .Lib;
2327 start_addr: u64,39 const is_dyn_lib = options.link_mode == .Dynamic and is_lib;
2328 end_addr: u64,40 const is_exe_or_dyn_lib = is_dyn_lib or options.output_mode == .Exe;
2329 ) []const macho.data_in_code_entry {41 const stack_size = options.stack_size_override orelse 0;
2330 const Predicate = struct {42 const is_debug_build = options.optimize_mode == .Debug;
2331 addr: u64,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 {45 const id_symlink_basename = "zld.id";
2334 return dice.offset >= self.addr;
2335 }
2336 };
233746
2338 const start = lsearch(macho.data_in_code_entry, dices, Predicate{ .addr = start_addr });47 var man: Cache.Manifest = undefined;
2339 const end = lsearch(macho.data_in_code_entry, dices[start..], Predicate{ .addr = end_addr }) + start;48 defer if (!options.disable_lld_caching) man.deinit();
234049
2341 return dices[start..end];50 var digest: [Cache.hex_digest_len]u8 = undefined;
2342 }
234351
2344 fn writeDataInCode(self: *Zld) !void {52 if (!options.disable_lld_caching) {
2345 var out_dice = std.ArrayList(macho.data_in_code_entry).init(self.gpa);53 man = comp.cache_parent.obtain();
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;
237154
2372 for (filtered_dice) |single| {55 // We are about to obtain this lock, so here we give other processes a chance first.
2373 const offset = math.cast(u32, single.offset - source_addr + base) orelse56 macho_file.base.releaseLock();
2374 return error.Overflow;
2375 out_dice.appendAssumeCapacity(.{
2376 .offset = offset,
2377 .length = single.length,
2378 .kind = single.kind,
2379 });
2380 }
2381 }
2382 }
238357
2384 const seg = self.getLinkeditSegmentPtr();58 comptime assert(Compilation.link_hash_implementation_version == 10);
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;
239059
2391 const buffer = try self.gpa.alloc(u8, math.cast(usize, needed_size_aligned) orelse return error.Overflow);60 for (options.objects) |obj| {
2392 defer self.gpa.free(buffer);61 _ = try man.addFile(obj.path, null);
2393 {62 man.hash.add(obj.must_link);
2394 const src = mem.sliceAsBytes(out_dice.items);
2395 @memcpy(buffer[0..src.len], src);
2396 @memset(buffer[src.len..], 0);
2397 }63 }
239864 for (comp.c_object_table.keys()) |key| {
2399 log.debug("writing data-in-code from 0x{x} to 0x{x}", .{ offset, offset + needed_size_aligned });65 _ = try man.addFile(key.status.success.object_path, null);
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 }
2443 }66 }
244467 try man.addOptionalFile(module_obj_path);
2445 var exports = std.ArrayList(macho.nlist_64).init(gpa);68 // We can skip hashing libc and libc++ components that we are in charge of building from Zig
2446 defer exports.deinit();69 // installation sources because they are always a product of the compiler version + target information.
244770 man.hash.add(stack_size);
2448 for (self.globals.items) |global| {71 man.hash.addOptional(options.pagezero_size);
2449 const sym = self.getSymbol(global);72 man.hash.addOptional(options.headerpad_size);
2450 if (sym.undf()) continue; // import, skip73 man.hash.add(options.headerpad_max_install_names);
2451 if (sym.n_desc == N_DEAD) continue;74 man.hash.add(gc_sections);
245275 man.hash.add(options.dead_strip_dylibs);
2453 var out_sym = sym;76 man.hash.add(options.strip);
2454 out_sym.n_strx = try self.strtab.insert(gpa, self.getSymbolName(global));77 man.hash.addListOfBytes(options.lib_dirs);
2455 try exports.append(out_sym);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);
2456 }84 }
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);90 // We don't actually care whether it's a cache hit or miss; we just
2459 defer imports.deinit();91 // need the digest and the lock.
246092 _ = try man.hit();
2461 var imports_table = std.AutoHashMap(SymbolWithLoc, u32).init(gpa);93 digest = man.final();
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 }
247494
2475 // We generate stabs last in order to ensure that the strtab always has debug info95 var prev_digest_buf: [digest.len]u8 = undefined;
2476 // strings trailing96 const prev_digest: []u8 = Cache.readSmallFile(
2477 if (!self.options.strip) {97 directory.handle,
2478 for (self.objects.items) |object| {98 id_symlink_basename,
2479 try self.generateSymbolStabs(object, &locals);99 &prev_digest_buf,
2480 }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;
2481 }115 }
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));121 // We are about to change the output file to be different, so we invalidate the build hash now.
2484 const nexports = @as(u32, @intCast(exports.items.len));122 directory.handle.deleteFile(id_symlink_basename) catch |err| switch (err) {
2485 const nimports = @as(u32, @intCast(imports.items.len));123 error.FileNotFound => {},
2486 const nsyms = nlocals + nexports + nimports;124 else => |e| return e,
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,
2513 };125 };
2514 }126 }
2515127
2516 fn writeStrtab(self: *Zld) !void {128 if (options.output_mode == .Obj) {
2517 const seg = self.getLinkeditSegmentPtr();129 // LLD's MachO driver does not support the equivalent of `-r` so we do a simple file copy
2518 const offset = seg.fileoff + seg.filesize;130 // here. TODO: think carefully about how we can avoid this redundant operation when doing
2519 assert(mem.isAlignedGeneric(u64, offset, @alignOf(u64)));131 // build-obj. See also the corresponding TODO in linkAsArchive.
2520 const needed_size = self.strtab.buffer.items.len;132 const the_object_path = blk: {
2521 const needed_size_aligned = mem.alignForward(u64, needed_size, @alignOf(u64));133 if (options.objects.len != 0) {
2522 seg.filesize = offset + needed_size_aligned - seg.fileoff;134 break :blk options.objects[0].path;
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).?);
2573 }135 }
2574 }
2575136
2576 if (self.getSectionByName("__DATA_CONST", "__got")) |sect_id| {137 if (comp.c_object_table.count() != 0)
2577 const got = &self.sections.items(.header)[sect_id];138 break :blk comp.c_object_table.keys()[0].status.success.object_path;
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 }
2588139
2589 if (self.getSectionByName("__DATA", "__la_symbol_ptr")) |sect_id| {140 if (module_obj_path) |p|
2590 const la_symbol_ptr = &self.sections.items(.header)[sect_id];141 break :blk p;
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 }
2598142
2599 const padding = math.cast(usize, needed_size_aligned - needed_size) orelse return error.Overflow;143 // TODO I think this is unreachable. Audit this situation when solving the above TODO
2600 if (padding > 0) {144 // regarding eliding redundant object -> object transformations.
2601 buf.appendNTimesAssumeCapacity(0, padding);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, .{});
2602 }151 }
152 } else {
153 const sub_path = options.emit.?.sub_path;
2603154
2604 assert(buf.items.len == needed_size_aligned);155 const old_file = macho_file.base.file; // TODO is this needed at all?
2605 try self.file.pwriteAll(buf.items, offset);156 defer macho_file.base.file = old_file;
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());
2658157
2659 log.debug("writing code signature from 0x{x} to 0x{x}", .{158 const file = try directory.handle.createFile(sub_path, .{
2660 self.codesig_cmd.dataoff,159 .truncate = true,
2661 self.codesig_cmd.dataoff + buffer.items.len,160 .read = true,
161 .mode = link.determineMode(options.*),
2662 });162 });
163 defer file.close();
164 macho_file.base.file = file;
2663165
2664 try self.file.pwriteAll(buffer.items, self.codesig_cmd.dataoff);166 // Index 0 is always a null symbol.
2665 }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.176 // Positional arguments to the linker such as object files and static archives.
2668 fn writeHeader(self: *Zld, ncmds: u32, sizeofcmds: u32) !void {177 var positionals = std.ArrayList(Compilation.LinkObject).init(arena);
2669 var header: macho.mach_header_64 = .{};178 try positionals.ensureUnusedCapacity(options.objects.len);
2670 header.flags = macho.MH_NOUNDEFS | macho.MH_DYLDLINK | macho.MH_PIE | macho.MH_TWOLEVEL;179 positionals.appendSliceAssumeCapacity(options.objects);
2671180
2672 switch (self.options.target.cpu.arch) {181 for (comp.c_object_table.keys()) |key| {
2673 .aarch64 => {182 try positionals.append(.{ .path = key.status.success.object_path });
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,
2682 }183 }
2683184
2684 switch (self.options.output_mode) {185 if (module_obj_path) |p| {
2685 .Exe => {186 try positionals.append(.{ .path = p });
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,
2694 }187 }
2695188
2696 if (self.getSectionByName("__DATA", "__thread_vars")) |sect_id| {189 if (comp.compiler_rt_lib) |lib| {
2697 header.flags |= macho.MH_HAS_TLV_DESCRIPTORS;190 try positionals.append(.{ .path = lib.full_object_path });
2698 if (self.sections.items(.header)[sect_id].size > 0) {
2699 header.flags |= macho.MH_HAS_TLV_DESCRIPTORS;
2700 }
2701 }191 }
2702192
2703 header.ncmds = ncmds;193 // libc++ dep
2704 header.sizeofcmds = sizeofcmds;194 if (options.link_libcpp) {
2705195 try positionals.ensureUnusedCapacity(2);
2706 log.debug("writing Mach-O header {}", .{header});196 positionals.appendAssumeCapacity(.{ .path = comp.libcxxabi_static_lib.?.full_object_path });
2707197 positionals.appendAssumeCapacity(.{ .path = comp.libcxx_static_lib.?.full_object_path });
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];
2782 }198 }
2783 }
2784199
2785 /// Returns symbol described by `sym_with_loc` descriptor.200 var libs = std.StringArrayHashMap(link.SystemLib).init(arena);
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 }
2794201
2795 /// Returns name of the symbol described by `sym_with_loc` descriptor.202 {
2796 pub fn getSymbolName(self: *const Zld, sym_with_loc: SymbolWithLoc) []const u8 {203 const vals = options.system_libs.values();
2797 if (sym_with_loc.getFile()) |file| {204 try libs.ensureUnusedCapacity(vals.len);
2798 const object = self.objects.items[file];205 for (vals) |v| libs.putAssumeCapacity(v.path.?, v);
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).?;
2803 }206 }
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.208 {
2815 /// Returns null otherwise.209 try libs.ensureUnusedCapacity(options.frameworks.len);
2816 pub fn getStubsAtomIndexForSymbol(self: *Zld, sym_with_loc: SymbolWithLoc) ?AtomIndex {210 for (options.frameworks) |v| libs.putAssumeCapacity(v.path, .{
2817 const index = self.stubs_table.get(sym_with_loc) orelse return null;211 .needed = v.needed,
2818 const entry = self.stubs.items[index];212 .weak = v.weak,
2819 return entry.atom_index;213 .path = v.path,
2820 }214 });
2821215 }
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 }
2841216
2842 pub fn generateSymbolStabs(self: *Zld, object: Object, locals: *std.ArrayList(macho.nlist_64)) !void {217 try MachO.resolveLibSystem(arena, comp, options.sysroot, target, options.lib_dirs, &libs);
2843 log.debug("generating stabs for '{s}'", .{object.name});
2844218
2845 const gpa = self.gpa;219 if (options.verbose_link) {
2846 var debug_info = object.parseDwarfInfo();220 var argv = std.ArrayList([]const u8).init(arena);
2847221
2848 var lookup = DwarfInfo.AbbrevLookupTable.init(gpa);222 try argv.append("zig");
2849 defer lookup.deinit();223 try argv.append("ld");
2850 try lookup.ensureUnusedCapacity(std.math.maxInt(u8));
2851224
2852 // We assume there is only one CU.225 if (is_exe_or_dyn_lib) {
2853 var cu_it = debug_info.getCompileUnitIterator();226 try argv.append("-dynamic");
2854 const compile_unit = while (try cu_it.next()) |cu| {227 }
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 };
2862228
2863 var abbrev_it = compile_unit.getAbbrevEntryIterator(debug_info);229 if (is_dyn_lib) {
2864 const cu_entry: DwarfInfo.AbbrevEntry = while (try abbrev_it.next(lookup)) |entry| switch (entry.tag) {230 try argv.append("-dylib");
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 };
2871231
2872 var maybe_tu_name: ?[]const u8 = null;232 if (options.install_name) |install_name| {
2873 var maybe_tu_comp_dir: ?[]const u8 = null;233 try argv.append("-install_name");
2874 var attr_it = cu_entry.getAttributeIterator(debug_info, compile_unit.cuh);234 try argv.append(install_name);
235 }
236 }
2875237
2876 while (try attr_it.next()) |attr| switch (attr.name) {238 if (options.sysroot) |syslibroot| {
2877 dwarf.AT.comp_dir => maybe_tu_comp_dir = attr.getString(debug_info, compile_unit.cuh) orelse continue,239 try argv.append("-syslibroot");
2878 dwarf.AT.name => maybe_tu_name = attr.getString(debug_info, compile_unit.cuh) orelse continue,240 try argv.append(syslibroot);
2879 else => continue,241 }
2880 };
2881242
2882 if (maybe_tu_name == null or maybe_tu_comp_dir == null) {243 for (options.rpath_list) |rpath| {
2883 log.debug("missing DWARF_AT_comp_dir and DWARF_AT_name attributes {s}; skipping", .{object.name});244 try argv.append("-rpath");
2884 return;245 try argv.append(rpath);
2885 }246 }
2886247
2887 const tu_name = maybe_tu_name.?;248 if (options.pagezero_size) |pagezero_size| {
2888 const tu_comp_dir = maybe_tu_comp_dir.?;249 try argv.append("-pagezero_size");
250 try argv.append(try std.fmt.allocPrint(arena, "0x{x}", .{pagezero_size}));
251 }
2889252
2890 // Open scope253 if (options.headerpad_size) |headerpad_size| {
2891 try locals.ensureUnusedCapacity(3);254 try argv.append("-headerpad_size");
2892 locals.appendAssumeCapacity(.{255 try argv.append(try std.fmt.allocPrint(arena, "0x{x}", .{headerpad_size}));
2893 .n_strx = try self.strtab.insert(gpa, tu_comp_dir),256 }
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 });
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: {262 if (gc_sections) {
2917 var name_lookup = DwarfInfo.SubprogramLookupByName.init(gpa);263 try argv.append("-dead_strip");
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);
2944 }264 }
2945 }
2946265
2947 // Close scope266 if (options.dead_strip_dylibs) {
2948 try locals.append(.{267 try argv.append("-dead_strip_dylibs");
2949 .n_strx = 0,268 }
2950 .n_type = macho.N_SO,
2951 .n_sect = 0,
2952 .n_desc = 0,
2953 .n_value = 0,
2954 });
2955 }
2956269
2957 fn generateSymbolStabsForSymbol(270 if (options.entry) |entry| {
2958 self: *Zld,271 try argv.append("-e");
2959 atom_index: AtomIndex,272 try argv.append(entry);
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 };
2994 }273 }
2995 return buf[0..1];
2996 }
2997274
2998 const size: u64 = size: {275 for (options.objects) |obj| {
2999 if (object.header.flags & macho.MH_SUBSECTIONS_VIA_SYMBOLS != 0) {276 if (obj.must_link) {
3000 break :size self.getAtom(atom_index).size;277 try argv.append("-force_load");
278 }
279 try argv.append(obj.path);
3001 }280 }
3002281
3003 // Since we don't have subsections to work with, we need to infer the size of each function282 for (comp.c_object_table.keys()) |key| {
3004 // the slow way by scanning the debug info for matching symbol names and extracting283 try argv.append(key.status.success.object_path);
3005 // the symbol's DWARF_AT_low_pc and DWARF_AT_high_pc values.284 }
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];
3008285
3009 if (subprogram.addr <= source_sym.n_value and source_sym.n_value < subprogram.addr + subprogram.size) {286 if (module_obj_path) |p| {
3010 break :size subprogram.size;287 try argv.append(p);
3011 } else {
3012 log.debug("no stab found for {s}", .{sym_name});
3013 return buf[0..0];
3014 }288 }
3015 };
3016289
3017 buf[0] = .{290 if (comp.compiler_rt_lib) |lib| {
3018 .n_strx = 0,291 try argv.append(lib.full_object_path);
3019 .n_type = macho.N_BNSYM,292 }
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 };
3045293
3046 return buf;294 if (options.link_libcpp) {
3047 }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 {299 try argv.append("-o");
3050 log.debug("segments:", .{});300 try argv.append(full_out_path);
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 }
3061301
3062 fn logSections(self: *Zld) void {302 try argv.append("-lSystem");
3063 log.debug("sections:", .{});303 try argv.append("-lc");
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 }
3075304
3076 fn logSymAttributes(sym: macho.nlist_64, buf: []u8) []const u8 {305 for (options.system_libs.keys()) |l_name| {
3077 if (sym.sect()) {306 const info = options.system_libs.get(l_name).?;
3078 buf[0] = 's';307 const arg = if (info.needed)
3079 }308 try std.fmt.allocPrint(arena, "-needed-l{s}", .{l_name})
3080 if (sym.ext()) {309 else if (info.weak)
3081 if (sym.weakDef() or sym.pext()) {310 try std.fmt.allocPrint(arena, "-weak-l{s}", .{l_name})
3082 buf[1] = 'w';311 else
3083 } else {312 try std.fmt.allocPrint(arena, "-l{s}", .{l_name});
3084 buf[1] = 'e';313 try argv.append(arg);
3085 }314 }
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 {316 for (options.lib_dirs) |lib_dir| {
3097 var buf: [4]u8 = undefined;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:", .{});331 for (options.framework_dirs) |framework_dir| {
3102 for (self.objects.items, 0..) |object, id| {332 try argv.append(try std.fmt.allocPrint(arena, "-F{s}", .{framework_dir}));
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 });
3114 }333 }
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:", .{});335 if (is_dyn_lib and (options.allow_shlib_undefined orelse false)) {
3129 for (self.globals.items, 0..) |global, i| {336 try argv.append("-undefined");
3130 const sym = self.getSymbol(global);337 try argv.append("dynamic_lookup");
3131 if (sym.undf()) continue;338 }
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 }
3142339
3143 scoped_log.debug("imports:", .{});340 Compilation.dump_argv(argv.items);
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 });
3156 }341 }
3157342
3158 scoped_log.debug("GOT entries:", .{});343 var dependent_libs = std.fifo.LinearFifo(MachO.DylibReExportInfo, .Dynamic).init(arena);
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 }
3179344
3180 scoped_log.debug("__thread_ptrs entries:", .{});345 for (positionals.items) |obj| {
3181 for (self.tlv_ptr_entries.items, 0..) |entry, i| {346 const in_file = try std.fs.cwd().openFile(obj.path, .{});
3182 const atom_sym = entry.getAtomSymbol(self);347 defer in_file.close();
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 }
3192348
3193 scoped_log.debug("stubs entries:", .{});349 var parse_ctx = MachO.ParseErrorCtx.init(gpa);
3194 for (self.stubs.items, 0..) |entry, i| {350 defer parse_ctx.deinit();
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 }
3205351
3206 scoped_log.debug("thunks:", .{});352 macho_file.parsePositional(
3207 for (self.thunks.items, 0..) |thunk, i| {353 in_file,
3208 scoped_log.debug(" thunk({d})", .{i});354 obj.path,
3209 for (thunk.lookup.keys(), 0..) |target, j| {355 obj.must_link,
3210 const target_sym = self.getSymbol(target);356 &dependent_libs,
3211 const atom = self.getAtom(thunk.lookup.get(target).?);357 &parse_ctx,
3212 const atom_sym = self.getSymbol(atom.getSymbolWithLoc());358 ) catch |err| try macho_file.handleAndReportParseError(obj.path, err, &parse_ctx);
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 }
3220 }359 }
3221 }
3222360
3223 fn logAtoms(self: *Zld) void {361 for (libs.keys(), libs.values()) |path, lib| {
3224 log.debug("atoms:", .{});362 const in_file = try std.fs.cwd().openFile(path, .{});
3225 const slice = self.sections.slice();363 defer in_file.close();
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;
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) {380 try macho_file.parseDependentLibs(&dependent_libs);
3235 const atom = self.getAtom(atom_index);
3236 self.logAtom(atom_index, log);
3237381
3238 if (atom.next_index) |next_index| {382 var actions = std.ArrayList(MachO.ResolveAction).init(gpa);
3239 atom_index = next_index;383 defer actions.deinit();
3240 } else break;384 try macho_file.resolveSymbols(&actions);
3241 }385 if (macho_file.unresolved.count() > 0) {
386 try macho_file.reportUndefined();
387 return error.FlushFailure;
3242 }388 }
3243 }
3244389
3245 pub fn logAtom(self: *Zld, atom_index: AtomIndex, logger: anytype) void {390 for (macho_file.objects.items, 0..) |*object, object_id| {
3246 if (!build_options.enable_logging) return;391 object.splitIntoAtoms(macho_file, @as(u32, @intCast(object_id))) catch |err| switch (err) {
3247392 error.MissingEhFrameSection => try macho_file.reportParseError(
3248 const atom = self.getAtom(atom_index);393 object.name,
3249 const sym = self.getSymbol(atom.getSymbolWithLoc());394 "missing section: '__TEXT,__eh_frame' is required but could not be found",
3250 const sym_name = self.getSymbolName(atom.getSymbolWithLoc());395 .{},
3251 logger.debug(" ATOM({d}, %{d}, '{s}') @ {x} (sizeof({x}), alignof({x})) in object({?}) in sect({d})", .{396 ),
3252 atom_index,397 error.BadDwarfCfi => try macho_file.reportParseError(
3253 atom.sym_index,398 object.name,
3254 sym_name,399 "invalid DWARF: failed to parse '__TEXT,__eh_frame' section",
3255 sym.n_value,400 .{},
3256 atom.size,401 ),
3257 atom.alignment,402 else => |e| return e,
3258 atom.getFile(),403 };
3259 sym.n_sect,404 }
3260 });
3261405
3262 if (atom.getFile() != null) {406 if (gc_sections) {
3263 var it = Atom.getInnerSymbolsIterator(self, atom_index);407 try dead_strip.gcAtoms(macho_file);
3264 while (it.next()) |sym_loc| {408 }
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 }
3276409
3277 if (Atom.getSectionAlias(self, atom_index)) |sym_loc| {410 try macho_file.createDyldPrivateAtom();
3278 const alias = self.getSymbol(sym_loc);411 try macho_file.createTentativeDefAtoms();
3279 const alias_name = self.getSymbolName(sym_loc);
3280412
3281 logger.debug(" (%{d}, '{s}') @ {x} ({x})", .{413 if (macho_file.base.options.output_mode == .Exe) {
3282 sym_loc.sym_index,414 const global = macho_file.getEntryPoint().?;
3283 alias_name,415 if (macho_file.getSymbol(global).undf()) {
3284 alias.n_value,416 // We do one additional check here in case the entry point was found in one of the dylibs.
3285 0,417 // (I actually have no idea what this would imply but it is a possible outcome and so we
3286 });418 // support it.)
419 try macho_file.addStubEntry(global);
3287 }420 }
3288 }421 }
3289 }
3290};
3291
3292pub const N_DEAD: u16 = @as(u16, @bitCast(@as(i16, -1)));
3293422
3294const Section = struct {423 for (macho_file.objects.items) |object| {
3295 header: macho.section_64,424 for (object.atoms.items) |atom_index| {
3296 segment_index: u8,425 const atom = macho_file.getAtom(atom_index);
3297 first_atom_index: AtomIndex,426 const sym = macho_file.getSymbol(atom.getSymbolWithLoc());
3298 last_atom_index: AtomIndex,427 const header = macho_file.sections.items(.header)[sym.n_sect - 1];
3299};428 if (header.isZerofill()) continue;
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 }
3310429
3311 pub fn getTargetSymbolName(self: @This(), zld: *Zld) []const u8 {430 const relocs = Atom.getAtomRelocs(macho_file, atom_index);
3312 return zld.getSymbolName(self.target);431 try Atom.scanAtomRelocs(macho_file, atom_index, relocs);
3313 }432 }
433 }
3314434
3315 pub fn getAtomSymbol(self: @This(), zld: *Zld) macho.nlist_64 {435 try eh_frame.scanRelocs(macho_file);
3316 const atom = zld.getAtom(self.atom_index);436 try UnwindInfo.scanRelocs(macho_file);
3317 return zld.getSymbol(atom.getSymbolWithLoc());
3318 }
3319};
3320437
3321pub const SymbolWithLoc = extern struct {438 if (macho_file.dyld_stub_binder_index) |index|
3322 // Index into the respective symbol table.439 try macho_file.addGotEntry(macho_file.globals.items[index]);
3323 sym_index: u32,
3324440
3325 // 0 means it's a synthetic global.441 try calcSectionSizes(macho_file);
3326 file: u32 = 0,
3327442
3328 pub fn getFile(self: SymbolWithLoc) ?u32 {443 var unwind_info = UnwindInfo{ .gpa = gpa };
3329 if (self.file == 0) return null;444 defer unwind_info.deinit();
3330 return self.file - 1;445 try unwind_info.collect(macho_file);
3331 }
3332446
3333 pub fn eql(self: SymbolWithLoc, other: SymbolWithLoc) bool {447 try eh_frame.calcSectionSize(macho_file, &unwind_info);
3334 return self.file == other.file and self.sym_index == other.sym_index;448 unwind_info.calcSectionSize(macho_file);
3335 }
3336};
3337449
3338pub const SymbolResolver = struct {450 try pruneAndSortSections(macho_file);
3339 arena: Allocator,451 try createSegments(macho_file);
3340 table: std.StringHashMap(u32),452 try allocateSegments(macho_file);
3341 unresolved: std.AutoArrayHashMap(u32, void),
3342};
3343453
3344pub fn linkWithZld(macho_file: *MachO, comp: *Compilation, prog_node: *std.Progress.Node) link.File.FlushError!void {454 try macho_file.allocateSpecialSymbols();
3345 const tracy = trace(@src());
3346 defer tracy.end();
3347455
3348 const gpa = macho_file.base.allocator;456 if (build_options.enable_logging) {
3349 const options = &macho_file.base.options;457 macho_file.logSymtab();
3350 const target = options.target;458 macho_file.logSegments();
459 macho_file.logSections();
460 macho_file.logAtoms();
461 }
3351462
3352 var arena_allocator = std.heap.ArenaAllocator.init(gpa);463 try writeAtoms(macho_file);
3353 defer arena_allocator.deinit();464 if (macho_file.base.options.target.cpu.arch == .aarch64) try writeThunks(macho_file);
3354 const arena = arena_allocator.allocator();465 try writeDyldPrivateAtom(macho_file);
3355466
3356 const directory = options.emit.?.directory; // Just an alias to make it shorter to type.467 if (macho_file.stubs_section_index) |_| {
3357 const full_out_path = try directory.join(arena, &[_][]const u8{options.emit.?.sub_path});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 it477 try eh_frame.write(macho_file, &unwind_info);
3360 // will not be part of the linker line anyway.478 try unwind_info.write(macho_file);
3361 const module_obj_path: ?[]const u8 = if (options.module != null) blk: {479 try macho_file.writeLinkeditSegmentData();
3362 try macho_file.flushModule(comp, prog_node);
3363480
3364 if (fs.path.dirname(full_out_path)) |dirname| {481 // If the last section of __DATA segment is zerofill section, we need to ensure
3365 break :blk try fs.path.join(arena, &.{ dirname, macho_file.base.intermediary_basename.? });482 // that the free space between the end of the last non-zerofill section of __DATA
3366 } else {483 // segment and the beginning of __LINKEDIT segment is zerofilled as the loader will
3367 break :blk macho_file.base.intermediary_basename.?;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 }
3368 }502 }
3369 } else null;
3370503
3371 var sub_prog_node = prog_node.start("MachO Flush", 0);504 // Write code signature padding if required
3372 sub_prog_node.activate();505 var codesig: ?CodeSignature = if (MachO.requiresCodeSignature(&macho_file.base.options)) blk: {
3373 sub_prog_node.context.refresh();506 // Preallocate space for the code signature.
3374 defer sub_prog_node.end();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;521 // Write load commands
3377 const os_tag = target.os.tag;522 var lc_buffer = std.ArrayList(u8).init(arena);
3378 const abi = target.abi;523 const lc_writer = lc_buffer.writer();
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;
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;533 switch (macho_file.base.options.output_mode) {
3389 defer if (!options.disable_lld_caching) man.deinit();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) {558 try load_commands.writeRpathLCs(gpa, &macho_file.base.options, lc_writer);
3394 man = comp.cache_parent.obtain();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.575 const uuid_cmd_offset = @sizeOf(macho.mach_header_64) + @as(u32, @intCast(lc_buffer.items.len));
3397 macho_file.base.releaseLock();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| {584 if (codesig != null) {
3402 _ = try man.addFile(obj.path, null);585 try lc_writer.writeStruct(macho_file.codesig_cmd);
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);
3425 }586 }
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 just588 const ncmds = load_commands.calcNumOfLCs(lc_buffer.items);
3432 // need the digest and the lock.589 try macho_file.base.file.?.pwriteAll(lc_buffer.items, @sizeOf(macho.mach_header_64));
3433 _ = try man.hit();590 try macho_file.writeHeader(ncmds, @as(u32, @intCast(lc_buffer.items.len)));
3434 digest = man.final();591 try macho_file.writeUuid(comp, uuid_cmd_offset, codesig != null);
3435592
3436 var prev_digest_buf: [digest.len]u8 = undefined;593 if (codesig) |*csig| {
3437 const prev_digest: []u8 = Cache.readSmallFile(594 try macho_file.writeCodeSignature(comp, csig); // code signing always comes last
3438 directory.handle,595 try MachO.invalidateKernelCache(directory.handle, macho_file.base.options.emit.?.sub_path);
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;
3456 }596 }
3457 log.debug("MachO Zld prev_digest={s} new_digest={s}", .{597 }
3458 std.fmt.fmtSliceHexLower(prev_digest),
3459 std.fmt.fmtSliceHexLower(&digest),
3460 });
3461598
3462 // We are about to change the output file to be different, so we invalidate the build hash now.599 if (!options.disable_lld_caching) {
3463 directory.handle.deleteFile(id_symlink_basename) catch |err| switch (err) {600 // Update the file with the digest. If it fails we can continue; it only
3464 error.FileNotFound => {},601 // means that the next invocation will have an unnecessary cache miss.
3465 else => |e| return e,602 Cache.writeSmallFile(directory.handle, id_symlink_basename, &digest) catch |err| {
603 log.debug("failed to save linking hash digest file: {s}", .{@errorName(err)});
3466 };604 };
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();
3467 }614 }
615}
3468616
3469 if (options.output_mode == .Obj) {617fn createSegments(macho_file: *MachO) !void {
3470 // LLD's MachO driver does not support the equivalent of `-r` so we do a simple file copy618 const gpa = macho_file.base.allocator;
3471 // here. TODO: think carefully about how we can avoid this redundant operation when doing619 const pagezero_vmsize = macho_file.base.options.pagezero_size orelse MachO.default_pagezero_vmsize;
3472 // build-obj. See also the corresponding TODO in linkAsArchive.620 const page_size = MachO.getPageSize(macho_file.base.options.target.cpu.arch);
3473 const the_object_path = blk: {621 const aligned_pagezero_vmsize = mem.alignBackward(u64, pagezero_vmsize, page_size);
3474 if (options.objects.len != 0) {622 if (macho_file.base.options.output_mode != .Lib and aligned_pagezero_vmsize > 0) {
3475 break :blk options.objects[0].path;623 if (aligned_pagezero_vmsize != pagezero_vmsize) {
3476 }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)635 // __TEXT segment is non-optional
3479 break :blk comp.c_object_table.keys()[0].status.success.object_path;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|648 for (macho_file.sections.items(.header), 0..) |header, sect_id| {
3482 break :blk p;649 if (header.size == 0) continue; // empty section
3483650
3484 // TODO I think this is unreachable. Audit this situation when solving the above TODO651 const segname = header.segName();
3485 // regarding eliding redundant object -> object transformations.652 const segment_id = macho_file.getSegmentByName(segname) orelse blk: {
3486 return error.NoObjectsToLink;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;
3487 };663 };
3488 // This can happen when using --enable-cache and using the stage1 backend. In this case664 const segment = &macho_file.segments.items[segment_id];
3489 // we can skip the file copy.665 segment.cmdsize += @sizeOf(macho.section_64);
3490 if (!mem.eql(u8, the_object_path, full_out_path)) {666 segment.nsects += 1;
3491 try fs.cwd().copyFile(the_object_path, fs.cwd(), full_out_path, .{});667 macho_file.sections.items(.segment_index)[sect_id] = segment_id;
3492 }668 }
3493 } else {
3494 const page_size = macho_file.page_size;
3495 const sub_path = options.emit.?.sub_path;
3496669
3497 const file = try directory.handle.createFile(sub_path, .{670 if (macho_file.getSegmentByName("__DATA_CONST")) |index| {
3498 .truncate = true,671 macho_file.data_const_segment_cmd_index = index;
3499 .read = true,672 }
3500 .mode = link.determineMode(options.*),
3501 });
3502 defer file.close();
3503673
3504 var zld = Zld{674 if (macho_file.getSegmentByName("__DATA")) |index| {
3505 .gpa = gpa,675 macho_file.data_segment_cmd_index = index;
3506 .file = file,676 }
3507 .page_size = macho_file.page_size,
3508 .options = options,
3509 };
3510 defer zld.deinit();
3511677
3512 try zld.atoms.append(gpa, Atom.empty); // AtomIndex at 0 is reserved as null atom678 // __LINKEDIT always comes last
3513 try zld.strtab.buffer.append(gpa, 0);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.691fn writeAtoms(macho_file: *MachO) !void {
3516 var positionals = std.ArrayList([]const u8).init(arena);692 const gpa = macho_file.base.allocator;
3517 try positionals.ensureUnusedCapacity(options.objects.len);693 const slice = macho_file.sections.slice();
3518694
3519 var must_link_archives = std.StringArrayHashMap(void).init(arena);695 for (slice.items(.first_atom_index), 0..) |first_atom_index, sect_id| {
3520 try must_link_archives.ensureUnusedCapacity(options.objects.len);696 const header = slice.items(.header)[sect_id];
697 if (header.isZerofill()) continue;
3521698
3522 for (options.objects) |obj| {699 var atom_index = first_atom_index orelse continue;
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 }
3530700
3531 for (comp.c_object_table.keys()) |key| {701 var buffer = try gpa.alloc(u8, math.cast(usize, header.size) orelse return error.Overflow);
3532 try positionals.append(key.status.success.object_path);702 defer gpa.free(buffer);
3533 }703 @memset(buffer, 0); // TODO with NOPs
3534704
3535 if (module_obj_path) |p| {705 log.debug("writing atoms in {s},{s}", .{ header.segName(), header.sectName() });
3536 try positionals.append(p);
3537 }
3538706
3539 if (comp.compiler_rt_lib) |lib| {707 while (true) {
3540 try positionals.append(lib.full_object_path);708 const atom = macho_file.getAtom(atom_index);
3541 }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++ dep717 log.debug(" (adding ATOM(%{d}, '{s}') from object({d}) to buffer)", .{
3544 if (options.link_libcpp) {718 atom.sym_index,
3545 try positionals.append(comp.libcxxabi_static_lib.?.full_object_path);719 macho_file.getSymbolName(atom.getSymbolWithLoc()),
3546 try positionals.append(comp.libcxx_static_lib.?.full_object_path);720 file,
3547 }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 {742 if (atom.next_index) |next_index| {
3552 const vals = options.system_libs.values();743 atom_index = next_index;
3553 try libs.ensureUnusedCapacity(vals.len);744 } else break;
3554 for (vals) |v| libs.putAssumeCapacity(v.path.?, v);
3555 }745 }
3556746
3557 {747 log.debug(" (writing at file offset 0x{x})", .{header.offset});
3558 try libs.ensureUnusedCapacity(options.frameworks.len);748 try macho_file.base.file.?.pwriteAll(buffer, header.offset);
3559 for (options.frameworks) |v| libs.putAssumeCapacity(v.path, .{749 }
3560 .needed = v.needed,750}
3561 .weak = v.weak,
3562 .path = v.path,
3563 });
3564 }
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) {764fn writeThunks(macho_file: *MachO) !void {
3569 var argv = std.ArrayList([]const u8).init(arena);765 assert(macho_file.base.options.target.cpu.arch == .aarch64);
766 const gpa = macho_file.base.allocator;
3570767
3571 try argv.append("zig");768 const sect_id = macho_file.text_section_index orelse return;
3572 try argv.append("ld");769 const header = macho_file.sections.items(.header)[sect_id];
3573770
3574 if (is_exe_or_dyn_lib) {771 for (macho_file.thunks.items, 0..) |*thunk, i| {
3575 try argv.append("-dynamic");772 if (thunk.getSize() == 0) continue;
3576 }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) {785fn writePointerEntries(macho_file: *MachO, sect_id: u8, table: anytype) !void {
3579 try argv.append("-dylib");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| {799fn writeStubs(macho_file: *MachO) !void {
3582 try argv.append("-install_name");800 const gpa = macho_file.base.allocator;
3583 try argv.append(install_name);801 const cpu_arch = macho_file.base.options.target.cpu.arch;
3584 }802 const stubs_header = macho_file.sections.items(.header)[macho_file.stubs_section_index.?];
3585 }803 const la_symbol_ptr_header = macho_file.sections.items(.header)[macho_file.la_symbol_ptr_section_index.?];
3586804
3587 if (options.sysroot) |syslibroot| {805 const capacity = math.cast(usize, stubs_header.size) orelse return error.Overflow;
3588 try argv.append("-syslibroot");806 var buffer = try std.ArrayList(u8).initCapacity(gpa, capacity);
3589 try argv.append(syslibroot);807 defer buffer.deinit();
3590 }
3591808
3592 for (options.rpath_list) |rpath| {809 for (0..macho_file.stub_table.count()) |index| {
3593 try argv.append("-rpath");810 try stubs.writeStubCode(.{
3594 try argv.append(rpath);811 .cpu_arch = cpu_arch,
3595 }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| {817 log.debug("writing __TEXT,__stubs contents at file offset 0x{x}", .{stubs_header.offset});
3598 try argv.append("-pagezero_size");818 try macho_file.base.file.?.pwriteAll(buffer.items, stubs_header.offset);
3599 try argv.append(try std.fmt.allocPrint(arena, "0x{x}", .{pagezero_size}));819}
3600 }
3601820
3602 if (options.headerpad_size) |headerpad_size| {821fn writeStubHelpers(macho_file: *MachO) !void {
3603 try argv.append("-headerpad_size");822 const gpa = macho_file.base.allocator;
3604 try argv.append(try std.fmt.allocPrint(arena, "0x{x}", .{headerpad_size}));823 const cpu_arch = macho_file.base.options.target.cpu.arch;
3605 }824 const stub_helper_header = macho_file.sections.items(.header)[macho_file.stub_helper_section_index.?];
3606825
3607 if (options.headerpad_max_install_names) {826 const capacity = math.cast(usize, stub_helper_header.size) orelse return error.Overflow;
3608 try argv.append("-headerpad_max_install_names");827 var buffer = try std.ArrayList(u8).initCapacity(gpa, capacity);
3609 }828 defer buffer.deinit();
3610829
3611 if (gc_sections) {830 {
3612 try argv.append("-dead_strip");831 const dyld_private_addr = blk: {
3613 }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) {848 for (0..macho_file.stub_table.count()) |index| {
3616 try argv.append("-dead_strip_dylibs");849 const source_addr = stub_helper_header.addr + stubs.stubHelperPreambleSize(cpu_arch) +
3617 }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| {858 log.debug("writing __TEXT,__stub_helper contents at file offset 0x{x}", .{
3620 try argv.append("-e");859 stub_helper_header.offset,
3621 try argv.append(entry);860 });
3622 }861 try macho_file.base.file.?.pwriteAll(buffer.items, stub_helper_header.offset);
862}
3623863
3624 for (options.objects) |obj| {864fn writeLaSymbolPtrs(macho_file: *MachO) !void {
3625 try argv.append(obj.path);865 const gpa = macho_file.base.allocator;
3626 }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| {870 const capacity = math.cast(usize, la_symbol_ptr_header.size) orelse return error.Overflow;
3629 try argv.append(key.status.success.object_path);871 var buffer = try std.ArrayList(u8).initCapacity(gpa, capacity);
3630 }872 defer buffer.deinit();
3631873
3632 if (module_obj_path) |p| {874 for (0..macho_file.stub_table.count()) |index| {
3633 try argv.append(p);875 const target_addr = stub_helper_header.addr + stubs.stubHelperPreambleSize(cpu_arch) +
3634 }876 stubs.stubHelperSize(cpu_arch) * index;
877 buffer.writer().writeIntLittle(u64, target_addr) catch unreachable;
878 }
3635879
3636 if (comp.compiler_rt_lib) |lib| {880 log.debug("writing __DATA,__la_symbol_ptr contents at file offset 0x{x}", .{
3637 try argv.append(lib.full_object_path);881 la_symbol_ptr_header.offset,
3638 }882 });
883 try macho_file.base.file.?.pwriteAll(buffer.items, la_symbol_ptr_header.offset);
884}
3639885
3640 if (options.link_libcpp) {886fn pruneAndSortSections(macho_file: *MachO) !void {
3641 try argv.append(comp.libcxxabi_static_lib.?.full_object_path);887 const Entry = struct {
3642 try argv.append(comp.libcxx_static_lib.?.full_object_path);888 index: u8,
3643 }
3644889
3645 try argv.append("-o");890 pub fn lessThan(ctx: *MachO, lhs: @This(), rhs: @This()) bool {
3646 try argv.append(full_out_path);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");897 const gpa = macho_file.base.allocator;
3649 try argv.append("-lc");
3650898
3651 for (options.system_libs.keys()) |l_name| {899 var entries = try std.ArrayList(Entry).initCapacity(gpa, macho_file.sections.slice().len);
3652 const info = options.system_libs.get(l_name).?;900 defer entries.deinit();
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 }
3661901
3662 for (options.lib_dirs) |lib_dir| {902 for (0..macho_file.sections.slice().len) |index| {
3663 try argv.append(try std.fmt.allocPrint(arena, "-L{s}", .{lib_dir}));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 }
3664 }929 }
930 continue;
931 }
932 entries.appendAssumeCapacity(.{ .index = @intCast(index) });
933 }
3665934
3666 for (options.frameworks) |framework| {935 mem.sort(Entry, entries.items, macho_file, Entry.lessThan);
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 }
3676936
3677 for (options.framework_dirs) |framework_dir| {937 var slice = macho_file.sections.toOwnedSlice();
3678 try argv.append(try std.fmt.allocPrint(arena, "-F{s}", .{framework_dir}));938 defer slice.deinit(gpa);
3679 }
3680939
3681 if (is_dyn_lib and (options.allow_shlib_undefined orelse false)) {940 const backlinks = try gpa.alloc(u8, slice.len);
3682 try argv.append("-undefined");941 defer gpa.free(backlinks);
3683 try argv.append("dynamic_lookup");942 for (entries.items, 0..) |entry, i| {
3684 }943 backlinks[entry.index] = @as(u8, @intCast(i));
944 }
3685945
3686 for (must_link_archives.keys()) |lib| {946 try macho_file.sections.ensureTotalCapacity(gpa, entries.items.len);
3687 try argv.append(try std.fmt.allocPrint(arena, "-force_load {s}", .{lib}));947 for (entries.items) |entry| {
3688 }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.*];
3691 }969 }
970 }
971}
3692972
3693 var dependent_libs = std.fifo.LinearFifo(struct {973fn calcSectionSizes(macho_file: *MachO) !void {
3694 id: Dylib.Id,974 const slice = macho_file.sections.slice();
3695 parent: u16,975 for (slice.items(.header), 0..) |*header, sect_id| {
3696 }, .Dynamic).init(arena);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);981 var atom_index = slice.items(.first_atom_index)[sect_id] orelse continue;
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);
3702982
3703 var resolver = SymbolResolver{983 header.size = 0;
3704 .arena = arena,984 header.@"align" = 0;
3705 .table = std.StringHashMap(u32).init(arena),
3706 .unresolved = std.AutoArrayHashMap(u32, void).init(arena),
3707 };
3708 try zld.resolveSymbols(&resolver);
3709985
3710 if (resolver.unresolved.count() > 0) {986 while (true) {
3711 return error.UndefinedSymbolReference;987 const atom = macho_file.getAtom(atom_index);
3712 }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) {992 const sym = macho_file.getSymbolPtr(atom.getSymbolWithLoc());
3715 const entry_name = options.entry orelse load_commands.default_entry_point;993 sym.n_value = atom_offset;
3716 const global_index = resolver.table.get(entry_name).?; // Error was flagged earlier
3717 zld.entry_index = global_index;
3718 }
3719994
3720 for (zld.objects.items, 0..) |*object, object_id| {995 header.size += padding + atom.size;
3721 try object.splitIntoAtoms(&zld, @as(u32, @intCast(object_id)));996 header.@"align" = @max(header.@"align", atom.alignment);
3722 }
3723997
3724 if (gc_sections) {998 if (atom.next_index) |next_index| {
3725 try dead_strip.gcAtoms(&zld, &resolver);999 atom_index = next_index;
1000 } else break;
3726 }1001 }
1002 }
37271003
3728 try zld.createDyldPrivateAtom();1004 if (macho_file.text_section_index != null and macho_file.base.options.target.cpu.arch == .aarch64) {
3729 try zld.createTentativeDefAtoms();1005 // Create jump/branch range extenders if needed.
3730 try zld.createStubHelperPreambleAtom();1006 try thunks.createThunks(macho_file, macho_file.text_section_index.?);
1007 }
37311008
3732 if (zld.options.output_mode == .Exe) {1009 // Update offsets of all symbols contained within each Atom.
3733 const global = zld.getEntryPoint();1010 // We need to do this since our unwind info synthesiser relies on
3734 if (zld.getSymbol(global).undf()) {1011 // traversing the symbols when synthesising unwind info and DWARF CFI records.
3735 // We do one additional check here in case the entry point was found in one of the dylibs.1012 for (slice.items(.first_atom_index)) |first_atom_index| {
3736 // (I actually have no idea what this would imply but it is a possible outcome and so we1013 var atom_index = first_atom_index orelse continue;
3737 // support it.)
3738 try Atom.addStub(&zld, global);
3739 }
3740 }
37411014
3742 for (zld.objects.items) |object| {1015 while (true) {
3743 for (object.atoms.items) |atom_index| {1016 const atom = macho_file.getAtom(atom_index);
3744 const atom = zld.getAtom(atom_index);1017 const sym = macho_file.getSymbol(atom.getSymbolWithLoc());
3745 const sym = zld.getSymbol(atom.getSymbolWithLoc());1018
3746 const header = zld.sections.items(.header)[sym.n_sect - 1];1019 if (atom.getFile() != null) {
3747 if (header.isZerofill()) continue;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);1031 // If there is a section alias, update it now too
3750 try Atom.scanAtomRelocs(&zld, atom_index, relocs);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 }
3751 }1036 }
1037
1038 if (atom.next_index) |next_index| {
1039 atom_index = next_index;
1040 } else break;
3752 }1041 }
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);1050 if (macho_file.tlv_ptr_section_index) |sect_id| {
3755 try UnwindInfo.scanRelocs(&zld);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 };1064 if (macho_file.stub_helper_section_index) |sect_id| {
3762 defer unwind_info.deinit();1065 const header = &macho_file.sections.items(.header)[sect_id];
3763 try unwind_info.collect(&zld);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);1071 if (macho_file.la_symbol_ptr_section_index) |sect_id| {
3766 try unwind_info.calcSectionSize(&zld);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();1078fn allocateSegments(macho_file: *MachO) !void {
3769 try zld.createSegments();1079 const gpa = macho_file.base.allocator;
3770 try zld.allocateSegments();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) {1105fn allocateSegment(macho_file: *MachO, segment_index: u8, init_size: u64) !void {
3775 zld.logSymtab();1106 const segment = &macho_file.segments.items[segment_index];
3776 zld.logSegments();
3777 zld.logSections();
3778 zld.logAtoms();
3779 }
37801107
3781 try zld.writeAtoms();1108 if (mem.eql(u8, segment.segName(), "__PAGEZERO")) return; // allocated upon creation
3782 try eh_frame.write(&zld, &unwind_info);
3783 try unwind_info.write(&zld);
3784 try zld.writeLinkeditSegmentData();
37851109
3786 // If the last section of __DATA segment is zerofill section, we need to ensure1110 const base = getSegmentAllocBase(macho_file, segment_index);
3787 // that the free space between the end of the last non-zerofill section of __DATA1111 segment.vmaddr = base.vmaddr;
3788 // segment and the beginning of __LINKEDIT segment is zerofilled as the loader will1112 segment.fileoff = base.fileoff;
3789 // copy-paste this space into memory for quicker zerofill operation.1113 segment.filesize = init_size;
3790 if (zld.getSegmentByName("__DATA")) |data_seg_id| blk: {1114 segment.vmsize = init_size;
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 }
38081115
3809 // Write code signature padding if required1116 // Allocate the sections according to their alignment at the beginning of the segment.
3810 const requires_codesig = blk: {1117 const indexes = macho_file.getSectionIndexes(segment_index);
3811 if (options.entitlements) |_| break :blk true;1118 var start = init_size;
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);
38301119
3831 // Write load commands1120 const slice = macho_file.sections.slice();
3832 var lc_buffer = std.ArrayList(u8).init(arena);1121 for (slice.items(.header)[indexes.start..indexes.end], 0..) |*header, sect_id| {
3833 const lc_writer = lc_buffer.writer();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);1126 header.offset = if (header.isZerofill())
3836 try lc_writer.writeStruct(zld.dyld_info_cmd);1127 0
3837 try lc_writer.writeStruct(zld.function_starts_cmd);1128 else
3838 try lc_writer.writeStruct(zld.data_in_code_cmd);1129 @as(u32, @intCast(segment.fileoff + start_aligned));
3839 try lc_writer.writeStruct(zld.symtab_cmd);1130 header.addr = segment.vmaddr + start_aligned;
3840 try lc_writer.writeStruct(zld.dysymtab_cmd);1131
3841 try load_commands.writeDylinkerLC(lc_writer);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) {1135 log.debug("allocating local symbols in sect({d}, '{s},{s}')", .{
3844 const seg_id = zld.getSegmentByName("__TEXT").?;1136 n_sect,
3845 const seg = zld.segments.items[seg_id];1137 header.segName(),
3846 const global = zld.getEntryPoint();1138 header.sectName(),
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,
3861 });1139 });
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);1141 while (true) {
3868 try lc_writer.writeStruct(macho.source_version_command{1142 const atom = macho_file.getAtom(atom_index);
3869 .version = 0,1143 const sym = macho_file.getSymbolPtr(atom.getSymbolWithLoc());
3870 });1144 sym.n_value += header.addr;
3871 try load_commands.writeBuildVersionLC(zld.options, lc_writer);1145 sym.n_sect = n_sect;
38721146
3873 const uuid_cmd_offset = @sizeOf(macho.mach_header_64) + @as(u32, @intCast(lc_buffer.items.len));1147 log.debug(" ATOM(%{d}, '{s}') @{x}", .{
3874 try lc_writer.writeStruct(zld.uuid_cmd);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) {1174 if (atom.next_index) |next_index| {
3879 try lc_writer.writeStruct(zld.codesig_cmd);1175 atom_index = next_index;
1176 } else break;
1177 }
3880 }1178 }
38811179
3882 const ncmds = load_commands.calcNumOfLCs(lc_buffer.items);1180 start = start_aligned + header.size;
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);
38861181
3887 if (codesig) |*csig| {1182 if (!header.isZerofill()) {
3888 try zld.writeCodeSignature(comp, csig); // code signing always comes last1183 segment.filesize = start;
3889 try MachO.invalidateKernelCache(directory.handle, zld.options.emit.?.sub_path);
3890 }1184 }
1185 segment.vmsize = start;
3891 }1186 }
38921187
3893 if (!options.disable_lld_caching) {1188 const page_size = MachO.getPageSize(macho_file.base.options.target.cpu.arch);
3894 // Update the file with the digest. If it fails we can continue; it only1189 segment.filesize = mem.alignForward(u64, segment.filesize, page_size);
3895 // means that the next invocation will have an unnecessary cache miss.1190 segment.vmsize = mem.alignForward(u64, segment.vmsize, page_size);
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 }
3909}1191}
39101192
3911/// Binary search1193const std = @import("std");
3912pub fn bsearch(comptime T: type, haystack: []align(1) const T, predicate: anytype) usize {1194const build_options = @import("build_options");
3913 if (!@hasDecl(@TypeOf(predicate), "predicate"))1195const assert = std.debug.assert;
3914 @compileError("Predicate is required to define fn predicate(@This(), T) bool");1196const dwarf = std.dwarf;
39151197const fs = std.fs;
3916 var min: usize = 0;1198const log = std.log.scoped(.link);
3917 var max: usize = haystack.len;1199const macho = std.macho;
3918 while (min < max) {1200const math = std.math;
3919 const index = (min + max) / 2;1201const mem = std.mem;
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}
39291202
3930/// Linear search1203const aarch64 = @import("../../arch/aarch64/bits.zig");
3931pub fn lsearch(comptime T: type, haystack: []align(1) const T, predicate: anytype) usize {1204const calcUuid = @import("uuid.zig").calcUuid;
3932 if (!@hasDecl(@TypeOf(predicate), "predicate"))1205const dead_strip = @import("dead_strip.zig");
3933 @compileError("Predicate is required to define fn predicate(@This(), T) bool");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;1214const Allocator = mem.Allocator;
3936 while (i < haystack.len) : (i += 1) {1215const Archive = @import("Archive.zig");
3937 if (predicate.predicate(haystack[i])) break;1216const Atom = @import("Atom.zig");
3938 }1217const Cache = std.Build.Cache;
3939 return i;1218const CodeSignature = @import("CodeSignature.zig");
3940}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");...@@ -2,9 +2,10 @@ const std = @import("std");
2const fs = std.fs;2const fs = std.fs;
3const mem = std.mem;3const mem = std.mem;
4const log = std.log.scoped(.tapi);4const log = std.log.scoped(.tapi);
5const yaml = @import("tapi/yaml.zig");
56
6const Allocator = mem.Allocator;7const Allocator = mem.Allocator;
7const Yaml = @import("tapi/yaml.zig").Yaml;8const Yaml = yaml.Yaml;
89
9const VersionField = union(enum) {10const VersionField = union(enum) {
10 string: []const u8,11 string: []const u8,
...@@ -80,6 +81,30 @@ pub const Tbd = union(enum) {...@@ -80,6 +81,30 @@ pub const Tbd = union(enum) {
80 v3: TbdV3,81 v3: TbdV3,
81 v4: TbdV4,82 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
83 pub fn currentVersion(self: Tbd) ?VersionField {108 pub fn currentVersion(self: Tbd) ?VersionField {
84 return switch (self) {109 return switch (self) {
85 .v3 => |v3| v3.current_version,110 .v3 => |v3| v3.current_version,
...@@ -102,6 +127,11 @@ pub const Tbd = union(enum) {...@@ -102,6 +127,11 @@ pub const Tbd = union(enum) {
102 }127 }
103};128};
104129
130pub const TapiError = error{
131 NotLibStub,
132 FileTooBig,
133} || yaml.YamlError || std.fs.File.ReadError;
134
105pub const LibStub = struct {135pub const LibStub = struct {
106 /// Underlying memory for stub's contents.136 /// Underlying memory for stub's contents.
107 yaml: Yaml,137 yaml: Yaml,
...@@ -109,7 +139,7 @@ pub const LibStub = struct {...@@ -109,7 +139,7 @@ pub const LibStub = struct {
109 /// Typed contents of the tbd file.139 /// Typed contents of the tbd file.
110 inner: []Tbd,140 inner: []Tbd,
111141
112 pub fn loadFromFile(allocator: Allocator, file: fs.File) !LibStub {142 pub fn loadFromFile(allocator: Allocator, file: fs.File) TapiError!LibStub {
113 const source = try file.readToEndAlloc(allocator, std.math.maxInt(u32));143 const source = try file.readToEndAlloc(allocator, std.math.maxInt(u32));
114 defer allocator.free(source);144 defer allocator.free(source);
115145