authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2024-01-10 19:39:40+01:00
committergravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2024-01-24 12:34:38+01:00
log0c171afab003f7d7dfde8491141e00cac7e99c21
tree4873f24bd6468489045beada751540f82c5d956f
parent7588eeccea02b155f934a453cc47d8641686ab22

macho: parse an input object file!


8 files changed, 797 insertions(+), 431 deletions(-)

src/link/MachO.zig+630-305
......@@ -1,4 +1,4 @@
1base: File,
1base: link.File,
22
33/// If this is not null, an object file is created by LLVM and emitted to zcu_object_sub_path.
44llvm_object: ?*LlvmObject = null,
......@@ -6,6 +6,27 @@ llvm_object: ?*LlvmObject = null,
66/// Debug symbols bundle (or dSym).
77d_sym: ?DebugSymbols = null,
88
9/// A list of all input files.
10/// Index of each input file also encodes the priority or precedence of one input file
11/// over another.
12files: std.MultiArrayList(File.Entry) = .{},
13internal_object: ?File.Index = null,
14objects: std.ArrayListUnmanaged(File.Index) = .{},
15dylibs: std.ArrayListUnmanaged(File.Index) = .{},
16
17segments: std.ArrayListUnmanaged(macho.segment_command_64) = .{},
18sections: std.MultiArrayList(Section) = .{},
19
20symbols: std.ArrayListUnmanaged(Symbol) = .{},
21symbols_extra: std.ArrayListUnmanaged(u32) = .{},
22globals: std.AutoHashMapUnmanaged(u32, Symbol.Index) = .{},
23/// This table will be populated after `scanRelocs` has run.
24/// Key is symbol index.
25undefs: std.AutoHashMapUnmanaged(Symbol.Index, std.ArrayListUnmanaged(Atom.Index)) = .{},
26/// Global symbols we need to resolve for the link to succeed.
27undefined_symbols: std.ArrayListUnmanaged(Symbol.Index) = .{},
28boundary_symbols: std.ArrayListUnmanaged(Symbol.Index) = .{},
29
930dyld_info_cmd: macho.dyld_info_command = .{},
1031symtab_cmd: macho.symtab_command = .{},
1132dysymtab_cmd: macho.dysymtab_command = .{},
......@@ -14,36 +35,46 @@ data_in_code_cmd: macho.linkedit_data_command = .{ .cmd = .DATA_IN_CODE },
1435uuid_cmd: macho.uuid_command = .{ .uuid = [_]u8{0} ** 16 },
1536codesig_cmd: macho.linkedit_data_command = .{ .cmd = .CODE_SIGNATURE },
1637
17segments: std.ArrayListUnmanaged(macho.segment_command_64) = .{},
18sections: std.MultiArrayList(Section) = .{},
19
20pagezero_segment_cmd_index: ?u8 = null,
21header_segment_cmd_index: ?u8 = null,
22text_segment_cmd_index: ?u8 = null,
23data_const_segment_cmd_index: ?u8 = null,
24data_segment_cmd_index: ?u8 = null,
25linkedit_segment_cmd_index: ?u8 = null,
26
27text_section_index: ?u8 = null,
28data_const_section_index: ?u8 = null,
29data_section_index: ?u8 = null,
30bss_section_index: ?u8 = null,
31thread_vars_section_index: ?u8 = null,
32thread_data_section_index: ?u8 = null,
33thread_bss_section_index: ?u8 = null,
34eh_frame_section_index: ?u8 = null,
35unwind_info_section_index: ?u8 = null,
36stubs_section_index: ?u8 = null,
37stub_helper_section_index: ?u8 = null,
38got_section_index: ?u8 = null,
39la_symbol_ptr_section_index: ?u8 = null,
40tlv_ptr_section_index: ?u8 = null,
41
42strtab: StringTable = .{},
38pagezero_seg_index: ?u8 = null,
39text_seg_index: ?u8 = null,
40linkedit_seg_index: ?u8 = null,
41data_sect_index: ?u8 = null,
42got_sect_index: ?u8 = null,
43stubs_sect_index: ?u8 = null,
44stubs_helper_sect_index: ?u8 = null,
45la_symbol_ptr_sect_index: ?u8 = null,
46tlv_ptr_sect_index: ?u8 = null,
47eh_frame_sect_index: ?u8 = null,
48unwind_info_sect_index: ?u8 = null,
49objc_stubs_sect_index: ?u8 = null,
4350
4451/// List of atoms that are either synthetic or map directly to the Zig source program.
4552atoms: std.ArrayListUnmanaged(Atom) = .{},
46
53thunks: std.ArrayListUnmanaged(Thunk) = .{},
54unwind_records: std.ArrayListUnmanaged(UnwindInfo.Record) = .{},
55
56/// String interning table
57strings: StringTable = .{},
58
59/// Output synthetic sections
60symtab: std.ArrayListUnmanaged(macho.nlist_64) = .{},
61strtab: std.ArrayListUnmanaged(u8) = .{},
62indsymtab: Indsymtab = .{},
63got: GotSection = .{},
64stubs: StubsSection = .{},
65stubs_helper: StubsHelperSection = .{},
66objc_stubs: ObjcStubsSection = .{},
67la_symbol_ptr: LaSymbolPtrSection = .{},
68tlv_ptr: TlvPtrSection = .{},
69rebase: RebaseSection = .{},
70bind: BindSection = .{},
71weak_bind: WeakBindSection = .{},
72lazy_bind: LazyBindSection = .{},
73export_trie: ExportTrieSection = .{},
74unwind_info: UnwindInfo = .{},
75
76/// Options
77/// SDK layout
4778sdk_layout: ?SdkLayout,
4879/// Size of the __PAGEZERO segment.
4980pagezero_vmsize: ?u64,
......@@ -62,6 +93,8 @@ entitlements: ?[]const u8,
6293compatibility_version: ?std.SemanticVersion,
6394/// Entry name
6495entry_name: ?[]const u8,
96platform: Platform,
97sdk_version: ?std.SemanticVersion,
6598
6699/// Hot-code swapping state.
67100hot_state: if (is_hot_update_compatible) HotUpdateState else struct {} = .{},
......@@ -144,6 +177,8 @@ pub fn createEmpty(
144177 .enabled => default_entry_symbol_name,
145178 .named => |name| name,
146179 },
180 .platform = Platform.fromTarget(target),
181 .sdk_version = if (options.darwin_sdk_layout) |layout| inferSdkVersion(comp, layout) else null,
147182 };
148183 if (use_llvm and comp.config.have_zcu) {
149184 self.llvm_object = try LlvmObject.create(arena, comp);
......@@ -156,9 +191,16 @@ pub fn createEmpty(
156191 .mode = link.File.determineMode(false, output_mode, link_mode),
157192 });
158193
159 // Index 0 is always a null symbol.
160 // try self.locals.append(gpa, null_sym);
161 try self.strtab.buffer.append(gpa, 0);
194 // Append null file
195 try self.files.append(gpa, .null);
196 // Atom at index 0 is reserved as null atom
197 try self.atoms.append(gpa, .{});
198 // Append empty string to string tables
199 try self.strings.buffer.append(gpa, 0);
200 try self.strtab.append(gpa, 0);
201 // Append null symbols
202 try self.symbols.append(gpa, .{});
203 try self.symbols_extra.append(gpa, 0);
162204
163205 // TODO: init
164206
......@@ -208,8 +250,71 @@ pub fn open(
208250 return createEmpty(arena, comp, emit, options);
209251}
210252
253pub fn deinit(self: *MachO) void {
254 const gpa = self.base.comp.gpa;
255
256 if (self.llvm_object) |llvm_object| llvm_object.deinit();
257
258 if (self.d_sym) |*d_sym| {
259 d_sym.deinit();
260 }
261
262 for (self.files.items(.tags), self.files.items(.data)) |tag, *data| switch (tag) {
263 .null => {},
264 .internal => data.internal.deinit(gpa),
265 .object => data.object.deinit(gpa),
266 .dylib => data.dylib.deinit(gpa),
267 };
268 self.files.deinit(gpa);
269 self.objects.deinit(gpa);
270 self.dylibs.deinit(gpa);
271
272 self.segments.deinit(gpa);
273 for (self.sections.items(.atoms)) |*list| {
274 list.deinit(gpa);
275 }
276 self.sections.deinit(gpa);
277
278 self.symbols.deinit(gpa);
279 self.symbols_extra.deinit(gpa);
280 self.globals.deinit(gpa);
281 {
282 var it = self.undefs.iterator();
283 while (it.next()) |entry| {
284 entry.value_ptr.deinit(gpa);
285 }
286 self.undefs.deinit(gpa);
287 }
288 self.undefined_symbols.deinit(gpa);
289 self.boundary_symbols.deinit(gpa);
290
291 self.strings.deinit(gpa);
292 self.symtab.deinit(gpa);
293 self.strtab.deinit(gpa);
294 self.got.deinit(gpa);
295 self.stubs.deinit(gpa);
296 self.objc_stubs.deinit(gpa);
297 self.tlv_ptr.deinit(gpa);
298 self.rebase.deinit(gpa);
299 self.bind.deinit(gpa);
300 self.weak_bind.deinit(gpa);
301 self.lazy_bind.deinit(gpa);
302 self.export_trie.deinit(gpa);
303 self.unwind_info.deinit(gpa);
304
305 self.atoms.deinit(gpa);
306 for (self.thunks.items) |*thunk| {
307 thunk.deinit(gpa);
308 }
309 self.thunks.deinit(gpa);
310 self.unwind_records.deinit(gpa);
311}
312
211313pub fn flush(self: *MachO, arena: Allocator, prog_node: *std.Progress.Node) link.File.FlushError!void {
212 // TODO: what else should we do in flush? Is it actually needed at all?
314 // TODO: I think this is just a temp and can be removed once we can emit static archives
315 if (self.base.isStaticLib() and build_options.have_llvm) {
316 return self.base.linkAsArchive(arena, prog_node);
317 }
213318 try self.flushModule(arena, prog_node);
214319}
215320
......@@ -219,10 +324,11 @@ pub fn flushModule(self: *MachO, arena: Allocator, prog_node: *std.Progress.Node
219324
220325 const comp = self.base.comp;
221326 const gpa = comp.gpa;
222 _ = gpa;
223327
224328 if (self.llvm_object) |llvm_object| {
225329 try self.base.emitLlvmObject(arena, llvm_object, prog_node);
330 // TODO: I think this is just a temp and can be removed once we can emit static archives
331 if (self.base.isStaticLib() and build_options.have_llvm) return;
226332 }
227333
228334 var sub_prog_node = prog_node.start("MachO Flush", 0);
......@@ -240,11 +346,55 @@ pub fn flushModule(self: *MachO, arena: Allocator, prog_node: *std.Progress.Node
240346 break :blk path;
241347 }
242348 } else null;
243 _ = module_obj_path;
244349
245350 // --verbose-link
246351 if (comp.verbose_link) try self.dumpArgv(comp);
247352
353 if (self.base.isStaticLib()) return self.flushStaticLib(comp, module_obj_path);
354 if (self.base.isObject()) return self.flushObject(comp, module_obj_path);
355
356 var positionals = std.ArrayList(Compilation.LinkObject).init(gpa);
357 defer positionals.deinit();
358
359 try positionals.ensureUnusedCapacity(comp.objects.len);
360 positionals.appendSliceAssumeCapacity(comp.objects);
361
362 // This is a set of object files emitted by clang in a single `build-exe` invocation.
363 // For instance, the implicit `a.o` as compiled by `zig build-exe a.c` will end up
364 // in this set.
365 try positionals.ensureUnusedCapacity(comp.c_object_table.keys().len);
366 for (comp.c_object_table.keys()) |key| {
367 positionals.appendAssumeCapacity(.{ .path = key.status.success.object_path });
368 }
369
370 if (module_obj_path) |path| try positionals.append(.{ .path = path });
371
372 // rpaths
373 var rpath_table = std.StringArrayHashMap(void).init(gpa);
374 defer rpath_table.deinit();
375 try rpath_table.ensureUnusedCapacity(self.base.rpath_list.len);
376
377 for (self.base.rpath_list) |rpath| {
378 _ = rpath_table.putAssumeCapacity(rpath, {});
379 }
380
381 for (positionals.items) |obj| {
382 self.parsePositional(obj.path, obj.must_link) catch |err| switch (err) {
383 error.MalformedObject,
384 error.MalformedArchive,
385 error.InvalidCpuArch,
386 error.InvalidTarget,
387 => continue, // already reported
388 else => |e| try self.reportParseError(
389 obj.path,
390 "unexpected error: parsing input file failed with error {s}",
391 .{@errorName(e)},
392 ),
393 };
394 }
395
396 state_log.debug("{}", .{self.dumpState()});
397
248398 @panic("TODO");
249399}
250400
......@@ -255,7 +405,6 @@ fn dumpArgv(self: *MachO, comp: *Compilation) !void {
255405 defer arena_allocator.deinit();
256406 const arena = arena_allocator.allocator();
257407
258 const target = self.base.comp.root_mod.resolved_target.result;
259408 const directory = self.base.emit.directory;
260409 const full_out_path = try directory.join(arena, &[_][]const u8{self.base.emit.sub_path});
261410 const module_obj_path: ?[]const u8 = if (self.base.zcu_object_sub_path) |path| blk: {
......@@ -309,18 +458,14 @@ fn dumpArgv(self: *MachO, comp: *Compilation) !void {
309458 }
310459 }
311460
312 {
313 const platform = Platform.fromTarget(target);
314 try argv.append("-platform_version");
315 try argv.append(@tagName(platform.os_tag));
316 try argv.append(try std.fmt.allocPrint(arena, "{}", .{platform.version}));
317
318 const sdk_version: ?std.SemanticVersion = self.inferSdkVersion();
319 if (sdk_version) |ver| {
320 try argv.append(try std.fmt.allocPrint(arena, "{d}.{d}", .{ ver.major, ver.minor }));
321 } else {
322 try argv.append(try std.fmt.allocPrint(arena, "{}", .{platform.version}));
323 }
461 try argv.append("-platform_version");
462 try argv.append(@tagName(self.platform.os_tag));
463 try argv.append(try std.fmt.allocPrint(arena, "{}", .{self.platform.version}));
464
465 if (self.sdk_version) |ver| {
466 try argv.append(try std.fmt.allocPrint(arena, "{d}.{d}", .{ ver.major, ver.minor }));
467 } else {
468 try argv.append(try std.fmt.allocPrint(arena, "{}", .{self.platform.version}));
324469 }
325470
326471 if (comp.sysroot) |syslibroot| {
......@@ -419,6 +564,26 @@ fn dumpArgv(self: *MachO, comp: *Compilation) !void {
419564 Compilation.dump_argv(argv.items);
420565}
421566
567fn flushStaticLib(self: *MachO, comp: *Compilation, module_obj_path: ?[]const u8) link.File.FlushError!void {
568 _ = comp;
569 _ = module_obj_path;
570
571 var err = try self.addErrorWithNotes(0);
572 try err.addMsg(self, "TODO implement flushStaticLib", .{});
573
574 return error.FlushFailure;
575}
576
577fn flushObject(self: *MachO, comp: *Compilation, module_obj_path: ?[]const u8) link.File.FlushError!void {
578 _ = comp;
579 _ = module_obj_path;
580
581 var err = try self.addErrorWithNotes(0);
582 try err.addMsg(self, "TODO implement flushObject", .{});
583
584 return error.FlushFailure;
585}
586
422587/// XNU starting with Big Sur running on arm64 is caching inodes of running binaries.
423588/// Any change to the binary will effectively invalidate the kernel's cache
424589/// resulting in a SIGKILL on each subsequent run. Since when doing incremental
......@@ -518,132 +683,60 @@ fn accessLibPath(
518683}
519684
520685const ParseError = error{
521 UnknownFileType,
686 MalformedObject,
687 MalformedArchive,
688 NotLibStub,
689 InvalidCpuArch,
522690 InvalidTarget,
523691 InvalidTargetFatLibrary,
524 DylibAlreadyExists,
525692 IncompatibleDylibVersion,
526693 OutOfMemory,
527694 Overflow,
528695 InputOutput,
529 MalformedArchive,
530 NotLibStub,
531696 EndOfStream,
532697 FileSystem,
533698 NotSupported,
534699} || std.os.SeekError || std.fs.File.OpenError || std.fs.File.ReadError || tapi.TapiError;
535700
536pub fn parsePositional(
537 self: *MachO,
538 file: std.fs.File,
539 path: []const u8,
540 must_link: bool,
541 dependent_libs: anytype,
542 ctx: *ParseErrorCtx,
543) ParseError!void {
701fn parsePositional(self: *MachO, path: []const u8, must_link: bool) ParseError!void {
544702 const tracy = trace(@src());
545703 defer tracy.end();
704 if (try Object.isObject(path)) {
705 try self.parseObject(path);
706 } else {
707 try self.parseLibrary(.{ .path = path }, must_link);
708 }
709}
546710
711fn parseLibrary(self: *MachO, lib: SystemLib, must_link: bool) ParseError!void {
547712 _ = self;
548 _ = file;
549 _ = path;
713 _ = lib;
550714 _ = must_link;
551 _ = dependent_libs;
552 _ = ctx;
553715}
554716
555pub fn deinit(self: *MachO) void {
556 const gpa = self.base.comp.gpa;
557
558 if (self.llvm_object) |llvm_object| llvm_object.deinit();
559
560 if (self.d_sym) |*d_sym| {
561 d_sym.deinit();
562 }
563
564 self.strtab.deinit(gpa);
565
566 self.segments.deinit(gpa);
567
568 for (self.sections.items(.free_list)) |*list| {
569 list.deinit(gpa);
570 }
571 self.sections.deinit(gpa);
572}
717fn parseObject(self: *MachO, path: []const u8) ParseError!void {
718 const tracy = trace(@src());
719 defer tracy.end();
573720
574fn freeAtom(self: *MachO, atom_index: Atom.Index) void {
575721 const gpa = self.base.comp.gpa;
576 log.debug("freeAtom {d}", .{atom_index});
577
578 // Remove any relocs and base relocs associated with this Atom
579 Atom.freeRelocations(self, atom_index);
580
581 const atom = self.getAtom(atom_index);
582 const sect_id = atom.getSymbol(self).n_sect - 1;
583 const free_list = &self.sections.items(.free_list)[sect_id];
584 var already_have_free_list_node = false;
585 {
586 var i: usize = 0;
587 // TODO turn free_list into a hash map
588 while (i < free_list.items.len) {
589 if (free_list.items[i] == atom_index) {
590 _ = free_list.swapRemove(i);
591 continue;
592 }
593 if (free_list.items[i] == atom.prev_index) {
594 already_have_free_list_node = true;
595 }
596 i += 1;
597 }
598 }
599
600 const maybe_last_atom_index = &self.sections.items(.last_atom_index)[sect_id];
601 if (maybe_last_atom_index.*) |last_atom_index| {
602 if (last_atom_index == atom_index) {
603 if (atom.prev_index) |prev_index| {
604 // TODO shrink the section size here
605 maybe_last_atom_index.* = prev_index;
606 } else {
607 maybe_last_atom_index.* = null;
608 }
609 }
610 }
611
612 if (atom.prev_index) |prev_index| {
613 const prev = self.getAtomPtr(prev_index);
614 prev.next_index = atom.next_index;
615
616 if (!already_have_free_list_node and prev.*.freeListEligible(self)) {
617 // The free list is heuristics, it doesn't have to be perfect, so we can ignore
618 // the OOM here.
619 free_list.append(gpa, prev_index) catch {};
620 }
621 } else {
622 self.getAtomPtr(atom_index).prev_index = null;
623 }
624
625 if (atom.next_index) |next_index| {
626 self.getAtomPtr(next_index).prev_index = atom.prev_index;
627 } else {
628 self.getAtomPtr(atom_index).next_index = null;
629 }
630
631 // Appending to free lists is allowed to fail because the free lists are heuristics based anyway.
632 const sym_index = atom.getSymbolIndex().?;
633
634 self.locals_free_list.append(gpa, sym_index) catch {};
635
636 // Try freeing GOT atom if this decl had one
637 self.got_table.freeEntry(gpa, .{ .sym_index = sym_index });
638
639 if (self.d_sym) |*d_sym| {
640 d_sym.swapRemoveRelocs(sym_index);
641 }
642
643 self.locals.items[sym_index].n_type = 0;
644 _ = self.atom_by_index_table.remove(sym_index);
645 log.debug(" adding local symbol index {d} to free list", .{sym_index});
646 self.getAtomPtr(atom_index).sym_index = 0;
722 const file = try std.fs.cwd().openFile(path, .{});
723 defer file.close();
724 const mtime: u64 = mtime: {
725 const stat = file.stat() catch break :mtime 0;
726 break :mtime @as(u64, @intCast(@divFloor(stat.mtime, 1_000_000_000)));
727 };
728 const data = try file.readToEndAlloc(gpa, std.math.maxInt(u32));
729 const index = @as(File.Index, @intCast(try self.files.addOne(gpa)));
730 self.files.set(index, .{ .object = .{
731 .path = try gpa.dupe(u8, path),
732 .mtime = mtime,
733 .data = data,
734 .index = index,
735 } });
736 try self.objects.append(gpa, index);
737
738 const object = self.getFile(index).?.object;
739 try object.parse(self);
647740}
648741
649742fn shrinkAtom(self: *MachO, atom_index: Atom.Index, new_block_size: u64) void {
......@@ -716,7 +809,7 @@ pub fn updateDecl(self: *MachO, mod: *Module, decl_index: InternPool.DeclIndex)
716809
717810fn updateLazySymbolAtom(
718811 self: *MachO,
719 sym: File.LazySymbol,
812 sym: link.File.LazySymbol,
720813 atom_index: Atom.Index,
721814 section_index: u8,
722815) !void {
......@@ -727,7 +820,7 @@ fn updateLazySymbolAtom(
727820 @panic("TODO updateLazySymbolAtom");
728821}
729822
730pub fn getOrCreateAtomForLazySymbol(self: *MachO, sym: File.LazySymbol) !Atom.Index {
823pub fn getOrCreateAtomForLazySymbol(self: *MachO, sym: link.File.LazySymbol) !Atom.Index {
731824 _ = self;
732825 _ = sym;
733826 @panic("TODO getOrCreateAtomForLazySymbol");
......@@ -763,7 +856,7 @@ pub fn updateExports(
763856 mod: *Module,
764857 exported: Module.Exported,
765858 exports: []const *Module.Export,
766) File.UpdateExportsError!void {
859) link.File.UpdateExportsError!void {
767860 if (build_options.skip_non_native and builtin.object_format != .macho) {
768861 @panic("Attempted to compile for object format that was disabled by build configuration");
769862 }
......@@ -795,7 +888,7 @@ pub fn freeDecl(self: *MachO, decl_index: InternPool.DeclIndex) void {
795888 @panic("TODO freeDecl");
796889}
797890
798pub fn getDeclVAddr(self: *MachO, decl_index: InternPool.DeclIndex, reloc_info: File.RelocInfo) !u64 {
891pub fn getDeclVAddr(self: *MachO, decl_index: InternPool.DeclIndex, reloc_info: link.File.RelocInfo) !u64 {
799892 assert(self.llvm_object == null);
800893 _ = decl_index;
801894 _ = reloc_info;
......@@ -872,94 +965,224 @@ fn findFreeSpace(self: *MachO, object_size: u64, min_alignment: u32) u64 {
872965 return start;
873966}
874967
968pub fn getTarget(self: MachO) std.Target {
969 return self.base.comp.root_mod.resolved_target.result;
970}
971
875972pub fn makeStaticString(bytes: []const u8) [16]u8 {
876973 var buf = [_]u8{0} ** 16;
877974 @memcpy(buf[0..bytes.len], bytes);
878975 return buf;
879976}
880977
881pub const ParseErrorCtx = struct {
882 arena_allocator: std.heap.ArenaAllocator,
883 detected_dylib_id: struct {
884 parent: u16,
885 required_version: u32,
886 found_version: u32,
887 },
888 detected_targets: std.ArrayList([]const u8),
978pub fn getFile(self: *MachO, index: File.Index) ?File {
979 const tag = self.files.items(.tags)[index];
980 return switch (tag) {
981 .null => null,
982 .internal => .{ .internal = &self.files.items(.data)[index].internal },
983 .object => .{ .object = &self.files.items(.data)[index].object },
984 .dylib => .{ .dylib = &self.files.items(.data)[index].dylib },
985 };
986}
889987
890 pub fn init(gpa: Allocator) ParseErrorCtx {
891 return .{
892 .arena_allocator = std.heap.ArenaAllocator.init(gpa),
893 .detected_dylib_id = undefined,
894 .detected_targets = std.ArrayList([]const u8).init(gpa),
988pub fn getInternalObject(self: *MachO) ?*InternalObject {
989 const index = self.internal_object orelse return null;
990 return self.getFile(index).?.internal;
991}
992
993pub fn addAtom(self: *MachO) error{OutOfMemory}!Atom.Index {
994 const index = @as(Atom.Index, @intCast(self.atoms.items.len));
995 const atom = try self.atoms.addOne(self.base.comp.gpa);
996 atom.* = .{};
997 return index;
998}
999
1000pub fn getAtom(self: *MachO, index: Atom.Index) ?*Atom {
1001 if (index == 0) return null;
1002 assert(index < self.atoms.items.len);
1003 return &self.atoms.items[index];
1004}
1005
1006pub fn addSymbol(self: *MachO) !Symbol.Index {
1007 const index = @as(Symbol.Index, @intCast(self.symbols.items.len));
1008 const symbol = try self.symbols.addOne(self.base.comp.gpa);
1009 symbol.* = .{};
1010 return index;
1011}
1012
1013pub fn getSymbol(self: *MachO, index: Symbol.Index) *Symbol {
1014 assert(index < self.symbols.items.len);
1015 return &self.symbols.items[index];
1016}
1017
1018pub fn addSymbolExtra(self: *MachO, extra: Symbol.Extra) !u32 {
1019 const fields = @typeInfo(Symbol.Extra).Struct.fields;
1020 try self.symbols_extra.ensureUnusedCapacity(self.base.comp.gpa, fields.len);
1021 return self.addSymbolExtraAssumeCapacity(extra);
1022}
1023
1024pub fn addSymbolExtraAssumeCapacity(self: *MachO, extra: Symbol.Extra) u32 {
1025 const index = @as(u32, @intCast(self.symbols_extra.items.len));
1026 const fields = @typeInfo(Symbol.Extra).Struct.fields;
1027 inline for (fields) |field| {
1028 self.symbols_extra.appendAssumeCapacity(switch (field.type) {
1029 u32 => @field(extra, field.name),
1030 else => @compileError("bad field type"),
1031 });
1032 }
1033 return index;
1034}
1035
1036pub fn getSymbolExtra(self: MachO, index: u32) ?Symbol.Extra {
1037 if (index == 0) return null;
1038 const fields = @typeInfo(Symbol.Extra).Struct.fields;
1039 var i: usize = index;
1040 var result: Symbol.Extra = undefined;
1041 inline for (fields) |field| {
1042 @field(result, field.name) = switch (field.type) {
1043 u32 => self.symbols_extra.items[i],
1044 else => @compileError("bad field type"),
8951045 };
1046 i += 1;
8961047 }
1048 return result;
1049}
8971050
898 pub fn deinit(ctx: *ParseErrorCtx) void {
899 ctx.arena_allocator.deinit();
900 ctx.detected_targets.deinit();
1051pub fn setSymbolExtra(self: *MachO, index: u32, extra: Symbol.Extra) void {
1052 assert(index > 0);
1053 const fields = @typeInfo(Symbol.Extra).Struct.fields;
1054 inline for (fields, 0..) |field, i| {
1055 self.symbols_extra.items[index + i] = switch (field.type) {
1056 u32 => @field(extra, field.name),
1057 else => @compileError("bad field type"),
1058 };
9011059 }
1060}
1061
1062const GetOrCreateGlobalResult = struct {
1063 found_existing: bool,
1064 index: Symbol.Index,
1065};
9021066
903 pub fn arena(ctx: *ParseErrorCtx) Allocator {
904 return ctx.arena_allocator.allocator();
1067pub fn getOrCreateGlobal(self: *MachO, off: u32) !GetOrCreateGlobalResult {
1068 const gpa = self.base.comp.gpa;
1069 const gop = try self.globals.getOrPut(gpa, off);
1070 if (!gop.found_existing) {
1071 const index = try self.addSymbol();
1072 const global = self.getSymbol(index);
1073 global.name = off;
1074 gop.value_ptr.* = index;
1075 }
1076 return .{
1077 .found_existing = gop.found_existing,
1078 .index = gop.value_ptr.*,
1079 };
1080}
1081
1082pub fn getGlobalByName(self: *MachO, name: []const u8) ?Symbol.Index {
1083 const off = self.strings.getOffset(name) orelse return null;
1084 return self.globals.get(off);
1085}
1086
1087pub fn addUnwindRecord(self: *MachO) !UnwindInfo.Record.Index {
1088 const index = @as(UnwindInfo.Record.Index, @intCast(self.unwind_records.items.len));
1089 const rec = try self.unwind_records.addOne(self.base.comp.gpa);
1090 rec.* = .{};
1091 return index;
1092}
1093
1094pub fn getUnwindRecord(self: *MachO, index: UnwindInfo.Record.Index) *UnwindInfo.Record {
1095 assert(index < self.unwind_records.items.len);
1096 return &self.unwind_records.items[index];
1097}
1098
1099pub fn addThunk(self: *MachO) !Thunk.Index {
1100 const index = @as(Thunk.Index, @intCast(self.thunks.items.len));
1101 const thunk = try self.thunks.addOne(self.base.comp.gpa);
1102 thunk.* = .{};
1103 return index;
1104}
1105
1106pub fn getThunk(self: *MachO, index: Thunk.Index) *Thunk {
1107 assert(index < self.thunks.items.len);
1108 return &self.thunks.items[index];
1109}
1110
1111pub fn eatPrefix(path: []const u8, prefix: []const u8) ?[]const u8 {
1112 if (mem.startsWith(u8, path, prefix)) return path[prefix.len..];
1113 return null;
1114}
1115
1116const ErrorWithNotes = struct {
1117 /// Allocated index in comp.link_errors array.
1118 index: usize,
1119
1120 /// Next available note slot.
1121 note_slot: usize = 0,
1122
1123 pub fn addMsg(
1124 err: ErrorWithNotes,
1125 macho_file: *MachO,
1126 comptime format: []const u8,
1127 args: anytype,
1128 ) error{OutOfMemory}!void {
1129 const comp = macho_file.base.comp;
1130 const gpa = comp.gpa;
1131 const err_msg = &comp.link_errors.items[err.index];
1132 err_msg.msg = try std.fmt.allocPrint(gpa, format, args);
1133 }
1134
1135 pub fn addNote(
1136 err: *ErrorWithNotes,
1137 macho_file: *MachO,
1138 comptime format: []const u8,
1139 args: anytype,
1140 ) error{OutOfMemory}!void {
1141 const comp = macho_file.base.comp;
1142 const gpa = comp.gpa;
1143 const err_msg = &comp.link_errors.items[err.index];
1144 assert(err.note_slot < err_msg.notes.len);
1145 err_msg.notes[err.note_slot] = .{ .msg = try std.fmt.allocPrint(gpa, format, args) };
1146 err.note_slot += 1;
9051147 }
9061148};
9071149
908pub fn handleAndReportParseError(
1150pub fn addErrorWithNotes(self: *MachO, note_count: usize) error{OutOfMemory}!ErrorWithNotes {
1151 const comp = self.base.comp;
1152 const gpa = comp.gpa;
1153 try comp.link_errors.ensureUnusedCapacity(gpa, 1);
1154 return self.addErrorWithNotesAssumeCapacity(note_count);
1155}
1156
1157fn addErrorWithNotesAssumeCapacity(self: *MachO, note_count: usize) error{OutOfMemory}!ErrorWithNotes {
1158 const comp = self.base.comp;
1159 const gpa = comp.gpa;
1160 const index = comp.link_errors.items.len;
1161 const err = comp.link_errors.addOneAssumeCapacity();
1162 err.* = .{ .msg = undefined, .notes = try gpa.alloc(link.File.ErrorMsg, note_count) };
1163 return .{ .index = index };
1164}
1165
1166pub fn reportParseError(
9091167 self: *MachO,
9101168 path: []const u8,
911 err: ParseError,
912 ctx: *const ParseErrorCtx,
1169 comptime format: []const u8,
1170 args: anytype,
9131171) error{OutOfMemory}!void {
914 const target = self.base.comp.root_mod.resolved_target.result;
915 const gpa = self.base.comp.gpa;
916 const cpu_arch = target.cpu.arch;
917 switch (err) {
918 error.DylibAlreadyExists => {},
919 error.IncompatibleDylibVersion => {
920 const parent = &self.dylibs.items[ctx.detected_dylib_id.parent];
921 try self.reportDependencyError(
922 if (parent.id) |id| id.name else parent.path,
923 path,
924 "incompatible dylib version: expected at least '{}', but found '{}'",
925 .{
926 load_commands.appleVersionToSemanticVersion(ctx.detected_dylib_id.required_version),
927 load_commands.appleVersionToSemanticVersion(ctx.detected_dylib_id.found_version),
928 },
929 );
930 },
931 error.UnknownFileType => try self.reportParseError(path, "unknown file type", .{}),
932 error.InvalidTarget, error.InvalidTargetFatLibrary => {
933 var targets_string = std.ArrayList(u8).init(gpa);
934 defer targets_string.deinit();
935
936 if (ctx.detected_targets.items.len > 1) {
937 try targets_string.writer().writeAll("(");
938 for (ctx.detected_targets.items) |t| {
939 try targets_string.writer().print("{s}, ", .{t});
940 }
941 try targets_string.resize(targets_string.items.len - 2);
942 try targets_string.writer().writeAll(")");
943 } else {
944 try targets_string.writer().writeAll(ctx.detected_targets.items[0]);
945 }
1172 var err = try self.addErrorWithNotes(1);
1173 try err.addMsg(self, format, args);
1174 try err.addNote(self, "while parsing {s}", .{path});
1175}
9461176
947 switch (err) {
948 error.InvalidTarget => try self.reportParseError(
949 path,
950 "invalid target: expected '{}', but found '{s}'",
951 .{ Platform.fromTarget(target).fmtTarget(cpu_arch), targets_string.items },
952 ),
953 error.InvalidTargetFatLibrary => try self.reportParseError(
954 path,
955 "invalid architecture in universal library: expected '{s}', but found '{s}'",
956 .{ @tagName(cpu_arch), targets_string.items },
957 ),
958 else => unreachable,
959 }
960 },
961 else => |e| try self.reportParseError(path, "{s}: parsing object failed", .{@errorName(e)}),
962 }
1177pub fn reportParseError2(
1178 self: *MachO,
1179 file_index: File.Index,
1180 comptime format: []const u8,
1181 args: anytype,
1182) error{OutOfMemory}!void {
1183 var err = try self.addErrorWithNotes(1);
1184 try err.addMsg(self, format, args);
1185 try err.addNote(self, "while parsing {}", .{self.getFile(file_index).?.fmtPath()});
9631186}
9641187
9651188fn reportMissingLibraryError(
......@@ -968,18 +1191,11 @@ fn reportMissingLibraryError(
9681191 comptime format: []const u8,
9691192 args: anytype,
9701193) error{OutOfMemory}!void {
971 const comp = self.base.comp;
972 const gpa = comp.gpa;
973 try comp.link_errors.ensureUnusedCapacity(gpa, 1);
974 const notes = try gpa.alloc(File.ErrorMsg, checked_paths.len);
975 errdefer gpa.free(notes);
976 for (checked_paths, notes) |path, *note| {
977 note.* = .{ .msg = try std.fmt.allocPrint(gpa, "tried {s}", .{path}) };
1194 var err = try self.addErrorWithNotes(checked_paths.len);
1195 try err.addMsg(self, format, args);
1196 for (checked_paths) |path| {
1197 try err.addNote(self, "tried {s}", .{path});
9781198 }
979 comp.link_errors.appendAssumeCapacity(.{
980 .msg = try std.fmt.allocPrint(gpa, format, args),
981 .notes = notes,
982 });
9831199}
9841200
9851201fn reportDependencyError(
......@@ -992,7 +1208,7 @@ fn reportDependencyError(
9921208 const comp = self.base.comp;
9931209 const gpa = comp.gpa;
9941210 try comp.link_errors.ensureUnusedCapacity(gpa, 1);
995 var notes = try std.ArrayList(File.ErrorMsg).initCapacity(gpa, 2);
1211 var notes = try std.ArrayList(link.File.ErrorMsg).initCapacity(gpa, 2);
9961212 defer notes.deinit();
9971213 if (path) |p| {
9981214 notes.appendAssumeCapacity(.{ .msg = try std.fmt.allocPrint(gpa, "while parsing {s}", .{p}) });
......@@ -1004,42 +1220,6 @@ fn reportDependencyError(
10041220 });
10051221}
10061222
1007pub fn reportParseError(
1008 self: *MachO,
1009 path: []const u8,
1010 comptime format: []const u8,
1011 args: anytype,
1012) error{OutOfMemory}!void {
1013 const comp = self.base.comp;
1014 const gpa = comp.gpa;
1015 try comp.link_errors.ensureUnusedCapacity(gpa, 1);
1016 var notes = try gpa.alloc(File.ErrorMsg, 1);
1017 errdefer gpa.free(notes);
1018 notes[0] = .{ .msg = try std.fmt.allocPrint(gpa, "while parsing {s}", .{path}) };
1019 comp.link_errors.appendAssumeCapacity(.{
1020 .msg = try std.fmt.allocPrint(gpa, format, args),
1021 .notes = notes,
1022 });
1023}
1024
1025pub fn reportUnresolvedBoundarySymbol(
1026 self: *MachO,
1027 sym_name: []const u8,
1028 comptime format: []const u8,
1029 args: anytype,
1030) error{OutOfMemory}!void {
1031 const comp = self.base.comp;
1032 const gpa = comp.gpa;
1033 try comp.link_errors.ensureUnusedCapacity(gpa, 1);
1034 var notes = try gpa.alloc(File.ErrorMsg, 1);
1035 errdefer gpa.free(notes);
1036 notes[0] = .{ .msg = try std.fmt.allocPrint(gpa, "while resolving {s}", .{sym_name}) };
1037 comp.link_errors.appendAssumeCapacity(.{
1038 .msg = try std.fmt.allocPrint(gpa, format, args),
1039 .notes = notes,
1040 });
1041}
1042
10431223pub fn reportUndefined(self: *MachO) error{OutOfMemory}!void {
10441224 const comp = self.base.comp;
10451225 const gpa = comp.gpa;
......@@ -1050,7 +1230,7 @@ pub fn reportUndefined(self: *MachO) error{OutOfMemory}!void {
10501230 const global = self.globals.items[global_index];
10511231 const sym_name = self.getSymbolName(global);
10521232
1053 var notes = try std.ArrayList(File.ErrorMsg).initCapacity(gpa, 1);
1233 var notes = try std.ArrayList(link.File.ErrorMsg).initCapacity(gpa, 1);
10541234 defer notes.deinit();
10551235
10561236 if (global.getFile()) |file| {
......@@ -1060,7 +1240,7 @@ pub fn reportUndefined(self: *MachO) error{OutOfMemory}!void {
10601240 notes.appendAssumeCapacity(.{ .msg = note });
10611241 }
10621242
1063 var err_msg = File.ErrorMsg{
1243 var err_msg = link.File.ErrorMsg{
10641244 .msg = try std.fmt.allocPrint(gpa, "undefined reference to symbol {s}", .{sym_name}),
10651245 };
10661246 err_msg.notes = try notes.toOwnedSlice();
......@@ -1164,6 +1344,145 @@ pub fn ptraceDetach(self: *MachO, pid: std.os.pid_t) !void {
11641344 self.hot_state.mach_task = null;
11651345}
11661346
1347pub fn dumpState(self: *MachO) std.fmt.Formatter(fmtDumpState) {
1348 return .{ .data = self };
1349}
1350
1351fn fmtDumpState(
1352 self: *MachO,
1353 comptime unused_fmt_string: []const u8,
1354 options: std.fmt.FormatOptions,
1355 writer: anytype,
1356) !void {
1357 _ = options;
1358 _ = unused_fmt_string;
1359 for (self.objects.items) |index| {
1360 const object = self.getFile(index).?.object;
1361 try writer.print("object({d}) : {} : has_debug({})", .{
1362 index,
1363 object.fmtPath(),
1364 object.hasDebugInfo(),
1365 });
1366 if (!object.alive) try writer.writeAll(" : ([*])");
1367 try writer.writeByte('\n');
1368 try writer.print("{}{}{}{}{}\n", .{
1369 object.fmtAtoms(self),
1370 object.fmtCies(self),
1371 object.fmtFdes(self),
1372 object.fmtUnwindRecords(self),
1373 object.fmtSymtab(self),
1374 });
1375 }
1376 // for (self.dylibs.items) |index| {
1377 // const dylib = self.getFile(index).?.dylib;
1378 // try writer.print("dylib({d}) : {s} : needed({}) : weak({})", .{
1379 // index,
1380 // dylib.path,
1381 // dylib.needed,
1382 // dylib.weak,
1383 // });
1384 // if (!dylib.isAlive(self)) try writer.writeAll(" : ([*])");
1385 // try writer.writeByte('\n');
1386 // try writer.print("{}\n", .{dylib.fmtSymtab(self)});
1387 // }
1388 if (self.getInternalObject()) |internal| {
1389 try writer.print("internal({d}) : internal\n", .{internal.index});
1390 try writer.print("{}{}\n", .{ internal.fmtAtoms(self), internal.fmtSymtab(self) });
1391 }
1392 try writer.writeAll("thunks\n");
1393 for (self.thunks.items, 0..) |thunk, index| {
1394 try writer.print("thunk({d}) : {}\n", .{ index, thunk.fmt(self) });
1395 }
1396 try writer.print("stubs\n{}\n", .{self.stubs.fmt(self)});
1397 try writer.print("objc_stubs\n{}\n", .{self.objc_stubs.fmt(self)});
1398 try writer.print("got\n{}\n", .{self.got.fmt(self)});
1399 try writer.print("tlv_ptr\n{}\n", .{self.tlv_ptr.fmt(self)});
1400 try writer.writeByte('\n');
1401 try writer.print("sections\n{}\n", .{self.fmtSections()});
1402 try writer.print("segments\n{}\n", .{self.fmtSegments()});
1403}
1404
1405fn fmtSections(self: *MachO) std.fmt.Formatter(formatSections) {
1406 return .{ .data = self };
1407}
1408
1409fn formatSections(
1410 self: *MachO,
1411 comptime unused_fmt_string: []const u8,
1412 options: std.fmt.FormatOptions,
1413 writer: anytype,
1414) !void {
1415 _ = options;
1416 _ = unused_fmt_string;
1417 const slice = self.sections.slice();
1418 for (slice.items(.header), slice.items(.segment_id), 0..) |header, seg_id, i| {
1419 try writer.print("sect({d}) : seg({d}) : {s},{s} : @{x} ({x}) : align({x}) : size({x})\n", .{
1420 i, seg_id, header.segName(), header.sectName(), header.offset, header.addr,
1421 header.@"align", header.size,
1422 });
1423 }
1424}
1425
1426fn fmtSegments(self: *MachO) std.fmt.Formatter(formatSegments) {
1427 return .{ .data = self };
1428}
1429
1430fn formatSegments(
1431 self: *MachO,
1432 comptime unused_fmt_string: []const u8,
1433 options: std.fmt.FormatOptions,
1434 writer: anytype,
1435) !void {
1436 _ = options;
1437 _ = unused_fmt_string;
1438 for (self.segments.items, 0..) |seg, i| {
1439 try writer.print("seg({d}) : {s} : @{x}-{x} ({x}-{x})\n", .{
1440 i, seg.segName(), seg.vmaddr, seg.vmaddr + seg.vmsize,
1441 seg.fileoff, seg.fileoff + seg.filesize,
1442 });
1443 }
1444}
1445
1446pub fn fmtSectType(tt: u8) std.fmt.Formatter(formatSectType) {
1447 return .{ .data = tt };
1448}
1449
1450fn formatSectType(
1451 tt: u8,
1452 comptime unused_fmt_string: []const u8,
1453 options: std.fmt.FormatOptions,
1454 writer: anytype,
1455) !void {
1456 _ = options;
1457 _ = unused_fmt_string;
1458 const name = switch (tt) {
1459 macho.S_REGULAR => "REGULAR",
1460 macho.S_ZEROFILL => "ZEROFILL",
1461 macho.S_CSTRING_LITERALS => "CSTRING_LITERALS",
1462 macho.S_4BYTE_LITERALS => "4BYTE_LITERALS",
1463 macho.S_8BYTE_LITERALS => "8BYTE_LITERALS",
1464 macho.S_16BYTE_LITERALS => "16BYTE_LITERALS",
1465 macho.S_LITERAL_POINTERS => "LITERAL_POINTERS",
1466 macho.S_NON_LAZY_SYMBOL_POINTERS => "NON_LAZY_SYMBOL_POINTERS",
1467 macho.S_LAZY_SYMBOL_POINTERS => "LAZY_SYMBOL_POINTERS",
1468 macho.S_SYMBOL_STUBS => "SYMBOL_STUBS",
1469 macho.S_MOD_INIT_FUNC_POINTERS => "MOD_INIT_FUNC_POINTERS",
1470 macho.S_MOD_TERM_FUNC_POINTERS => "MOD_TERM_FUNC_POINTERS",
1471 macho.S_COALESCED => "COALESCED",
1472 macho.S_GB_ZEROFILL => "GB_ZEROFILL",
1473 macho.S_INTERPOSING => "INTERPOSING",
1474 macho.S_DTRACE_DOF => "DTRACE_DOF",
1475 macho.S_THREAD_LOCAL_REGULAR => "THREAD_LOCAL_REGULAR",
1476 macho.S_THREAD_LOCAL_ZEROFILL => "THREAD_LOCAL_ZEROFILL",
1477 macho.S_THREAD_LOCAL_VARIABLES => "THREAD_LOCAL_VARIABLES",
1478 macho.S_THREAD_LOCAL_VARIABLE_POINTERS => "THREAD_LOCAL_VARIABLE_POINTERS",
1479 macho.S_THREAD_LOCAL_INIT_FUNCTION_POINTERS => "THREAD_LOCAL_INIT_FUNCTION_POINTERS",
1480 macho.S_INIT_FUNC_OFFSETS => "INIT_FUNC_OFFSETS",
1481 else => |x| return writer.print("UNKNOWN({x})", .{x}),
1482 };
1483 try writer.print("{s}", .{name});
1484}
1485
11671486const is_hot_update_compatible = switch (builtin.target.os.tag) {
11681487 .macos => true,
11691488 else => false,
......@@ -1171,32 +1490,14 @@ const is_hot_update_compatible = switch (builtin.target.os.tag) {
11711490
11721491const default_entry_symbol_name = "_main";
11731492
1174pub const base_tag: File.Tag = File.Tag.macho;
1493pub const base_tag: link.File.Tag = link.File.Tag.macho;
11751494pub const N_DEAD: u16 = @as(u16, @bitCast(@as(i16, -1)));
11761495pub const N_BOUNDARY: u16 = @as(u16, @bitCast(@as(i16, -2)));
11771496
1178pub const Section = struct {
1497const Section = struct {
11791498 header: macho.section_64,
1180 segment_index: u8,
1181 first_atom_index: ?Atom.Index = null,
1182 last_atom_index: ?Atom.Index = null,
1183
1184 /// A list of atoms that have surplus capacity. This list can have false
1185 /// positives, as functions grow and shrink over time, only sometimes being added
1186 /// or removed from the freelist.
1187 ///
1188 /// An atom has surplus capacity when its overcapacity value is greater than
1189 /// padToIdeal(minimum_atom_size). That is, when it has so
1190 /// much extra capacity, that we could fit a small new symbol in it, itself with
1191 /// ideal_capacity or more.
1192 ///
1193 /// Ideal capacity is defined by size + (size / ideal_factor).
1194 ///
1195 /// Overcapacity is measured by actual_capacity - ideal_capacity. Note that
1196 /// overcapacity can be negative. A simple way to have negative overcapacity is to
1197 /// allocate a fresh atom, which will have ideal capacity, and then grow it
1198 /// by 1 byte. It will then have -1 overcapacity.
1199 free_list: std.ArrayListUnmanaged(Atom.Index) = .{},
1499 segment_id: u8,
1500 atoms: std.ArrayListUnmanaged(Atom.Index) = .{},
12001501};
12011502
12021503const HotUpdateState = struct {
......@@ -1385,15 +1686,13 @@ pub inline fn appleVersionToSemanticVersion(version: u32) std.SemanticVersion {
13851686 };
13861687}
13871688
1388fn inferSdkVersion(self: *MachO) ?std.SemanticVersion {
1389 const comp = self.base.comp;
1689fn inferSdkVersion(comp: *Compilation, sdk_layout: SdkLayout) ?std.SemanticVersion {
13901690 const gpa = comp.gpa;
13911691
13921692 var arena_allocator = std.heap.ArenaAllocator.init(gpa);
13931693 defer arena_allocator.deinit();
13941694 const arena = arena_allocator.allocator();
13951695
1396 const sdk_layout = self.sdk_layout orelse return null;
13971696 const sdk_dir = switch (sdk_layout) {
13981697 .sdk => comp.sysroot.?,
13991698 .vendored => std.fs.path.join(arena, &.{ comp.zig_lib_directory.path.?, "libc", "darwin" }) catch return null,
......@@ -1402,6 +1701,7 @@ fn inferSdkVersion(self: *MachO) ?std.SemanticVersion {
14021701 return parseSdkVersion(ver);
14031702 } else |_| {
14041703 // Read from settings should always succeed when vendored.
1704 // TODO: convert to fatal linker error
14051705 if (sdk_layout == .vendored) @panic("zig installation bug: unable to parse SDK version");
14061706 }
14071707
......@@ -1470,6 +1770,15 @@ pub const default_pagezero_vmsize: u64 = 0x100000000;
14701770/// potential future extensions.
14711771pub const default_headerpad_size: u32 = 0x1000;
14721772
1773const SystemLib = struct {
1774 path: []const u8,
1775 needed: bool = false,
1776 weak: bool = false,
1777 hidden: bool = false,
1778 reexport: bool = false,
1779 must_link: bool = false,
1780};
1781
14731782const MachO = @This();
14741783
14751784const std = @import("std");
......@@ -1479,6 +1788,7 @@ const assert = std.debug.assert;
14791788const dwarf = std.dwarf;
14801789const fs = std.fs;
14811790const log = std.log.scoped(.link);
1791const state_log = std.log.scoped(.link_state);
14821792const macho = std.macho;
14831793const math = std.math;
14841794const mem = std.mem;
......@@ -1488,6 +1798,7 @@ const aarch64 = @import("../arch/aarch64/bits.zig");
14881798const calcUuid = @import("MachO/uuid.zig").calcUuid;
14891799const codegen = @import("../codegen.zig");
14901800const dead_strip = @import("MachO/dead_strip.zig");
1801const eh_frame = @import("MachO/eh_frame.zig");
14911802const fat = @import("MachO/fat.zig");
14921803const link = @import("../link.zig");
14931804const llvm_backend = @import("../codegen/llvm.zig");
......@@ -1496,12 +1807,14 @@ const tapi = @import("tapi.zig");
14961807const target_util = @import("../target.zig");
14971808const thunks = @import("MachO/thunks.zig");
14981809const trace = @import("../tracy.zig").trace;
1810const synthetic = @import("MachO/synthetic.zig");
14991811
15001812const Air = @import("../Air.zig");
15011813const Alignment = Atom.Alignment;
15021814const Allocator = mem.Allocator;
15031815const Archive = @import("MachO/Archive.zig");
15041816pub const Atom = @import("MachO/Atom.zig");
1817const BindSection = synthetic.BindSection;
15051818const Cache = std.Build.Cache;
15061819const CodeSignature = @import("MachO/CodeSignature.zig");
15071820const Compilation = @import("../Compilation.zig");
......@@ -1509,17 +1822,29 @@ pub const DebugSymbols = @import("MachO/DebugSymbols.zig");
15091822const Dwarf = File.Dwarf;
15101823const DwarfInfo = @import("MachO/DwarfInfo.zig");
15111824const Dylib = @import("MachO/Dylib.zig");
1512const File = link.File;
1825const ExportTrieSection = synthetic.ExportTrieSection;
1826const File = @import("MachO/file.zig").File;
1827const GotSection = synthetic.GotSection;
1828const Indsymtab = synthetic.Indsymtab;
1829const InternalObject = @import("MachO/InternalObject.zig");
1830const ObjcStubsSection = synthetic.ObjcStubsSection;
15131831const Object = @import("MachO/Object.zig");
1832const LazyBindSection = synthetic.LazyBindSection;
1833const LaSymbolPtrSection = synthetic.LaSymbolPtrSection;
15141834const LibStub = tapi.LibStub;
15151835const Liveness = @import("../Liveness.zig");
15161836const LlvmObject = @import("../codegen/llvm.zig").Object;
15171837const Md5 = std.crypto.hash.Md5;
15181838const Module = @import("../Module.zig");
15191839const InternPool = @import("../InternPool.zig");
1840const RebaseSection = synthetic.RebaseSection;
15201841const Relocation = @import("MachO/Relocation.zig");
15211842const StringTable = @import("StringTable.zig");
1522const TableSection = @import("table_section.zig").TableSection;
1523const Type = @import("../type.zig").Type;
1843const StubsSection = synthetic.StubsSection;
1844const StubsHelperSection = synthetic.StubsHelperSection;
1845const Symbol = @import("MachO/Symbol.zig");
1846const Thunk = thunks.Thunk;
1847const TlvPtrSection = synthetic.TlvPtrSection;
15241848const TypedValue = @import("../TypedValue.zig");
1525const Value = @import("../value.zig").Value;
1849const UnwindInfo = @import("MachO/UnwindInfo.zig");
1850const WeakBindSection = synthetic.WeakBindSection;
src/link/MachO/Atom.zig+1-1
......@@ -38,7 +38,7 @@ unwind_records: Loc = .{},
3838flags: Flags = .{},
3939
4040pub fn getName(self: Atom, macho_file: *MachO) [:0]const u8 {
41 return macho_file.string_intern.getAssumeExists(self.name);
41 return macho_file.strings.getAssumeExists(self.name);
4242}
4343
4444pub fn getFile(self: Atom, macho_file: *MachO) File {
src/link/MachO/Dylib.zig+1-1
......@@ -431,7 +431,7 @@ pub fn initSymbols(self: *Dylib, macho_file: *MachO) !void {
431431
432432 for (self.exports.items(.name)) |noff| {
433433 const name = self.getString(noff);
434 const off = try macho_file.string_intern.insert(gpa, name);
434 const off = try macho_file.strings.insert(gpa, name);
435435 const gop = try macho_file.getOrCreateGlobal(off);
436436 self.symbols.addOneAssumeCapacity().* = gop.index;
437437 }
src/link/MachO/Object.zig+138-96
......@@ -31,6 +31,14 @@ num_weak_bind_relocs: u32 = 0,
3131
3232output_symtab_ctx: MachO.SymtabCtx = .{},
3333
34pub fn isObject(path: []const u8) !bool {
35 const file = try std.fs.cwd().openFile(path, .{});
36 defer file.close();
37 const reader = file.reader();
38 const header = reader.readStruct(macho.mach_header_64) catch return false;
39 return header.filetype == macho.MH_OBJECT;
40}
41
3442pub fn deinit(self: *Object, allocator: Allocator) void {
3543 for (self.sections.items(.relocs), self.sections.items(.subsections)) |*relocs, *sub| {
3644 relocs.deinit(allocator);
......@@ -55,12 +63,25 @@ pub fn parse(self: *Object, macho_file: *MachO) !void {
5563 const tracy = trace(@src());
5664 defer tracy.end();
5765
58 const gpa = macho_file.base.allocator;
66 const gpa = macho_file.base.comp.gpa;
5967 var stream = std.io.fixedBufferStream(self.data);
6068 const reader = stream.reader();
6169
6270 self.header = try reader.readStruct(macho.mach_header_64);
6371
72 const this_cpu_arch: std.Target.Cpu.Arch = switch (self.header.?.cputype) {
73 macho.CPU_TYPE_ARM64 => .aarch64,
74 macho.CPU_TYPE_X86_64 => .x86_64,
75 else => |x| {
76 try macho_file.reportParseError2(self.index, "unknown cpu architecture: {d}", .{x});
77 return error.InvalidCpuArch;
78 },
79 };
80 if (macho_file.getTarget().cpu.arch != this_cpu_arch) {
81 try macho_file.reportParseError2(self.index, "invalid cpu architecture: {s}", .{@tagName(this_cpu_arch)});
82 return error.InvalidCpuArch;
83 }
84
6485 if (self.getLoadCommand(.SEGMENT_64)) |lc| {
6586 const sections = lc.getSections();
6687 try self.sections.ensureUnusedCapacity(gpa, sections.len);
......@@ -146,6 +167,20 @@ pub fn parse(self: *Object, macho_file: *MachO) !void {
146167 }
147168
148169 self.initPlatform();
170
171 if (self.platform) |platform| {
172 if (!macho_file.platform.eqlTarget(platform)) {
173 try macho_file.reportParseError2(self.index, "invalid platform: {}", .{
174 platform.fmtTarget(macho_file.getTarget().cpu.arch),
175 });
176 return error.InvalidTarget;
177 }
178 if (macho_file.platform.version.order(platform.version) != .lt) {
179 try macho_file.reportParseError2(self.index, "object file built for newer platform: {}", .{platform});
180 return error.InvalidTarget;
181 }
182 }
183
149184 try self.initDwarfInfo(macho_file);
150185
151186 for (self.atoms.items) |atom_index| {
......@@ -175,7 +210,7 @@ inline fn isLiteral(sect: macho.section_64) bool {
175210fn initSubsections(self: *Object, nlists: anytype, macho_file: *MachO) !void {
176211 const tracy = trace(@src());
177212 defer tracy.end();
178 const gpa = macho_file.base.allocator;
213 const gpa = macho_file.base.comp.gpa;
179214 const slice = self.sections.slice();
180215 for (slice.items(.header), slice.items(.subsections), 0..) |sect, *subsections, n_sect| {
181216 if (isLiteral(sect)) continue;
......@@ -243,7 +278,7 @@ fn initSubsections(self: *Object, nlists: anytype, macho_file: *MachO) !void {
243278fn initSections(self: *Object, nlists: anytype, macho_file: *MachO) !void {
244279 const tracy = trace(@src());
245280 defer tracy.end();
246 const gpa = macho_file.base.allocator;
281 const gpa = macho_file.base.comp.gpa;
247282 const slice = self.sections.slice();
248283
249284 try self.atoms.ensureUnusedCapacity(gpa, self.sections.items(.header).len);
......@@ -299,12 +334,12 @@ const AddAtomArgs = struct {
299334};
300335
301336fn addAtom(self: *Object, args: AddAtomArgs, macho_file: *MachO) !Atom.Index {
302 const gpa = macho_file.base.allocator;
337 const gpa = macho_file.base.comp.gpa;
303338 const atom_index = try macho_file.addAtom();
304339 const atom = macho_file.getAtom(atom_index).?;
305340 atom.file = self.index;
306341 atom.atom_index = atom_index;
307 atom.name = try macho_file.string_intern.insert(gpa, args.name);
342 atom.name = try macho_file.strings.insert(gpa, args.name);
308343 atom.n_sect = args.n_sect;
309344 atom.size = args.size;
310345 atom.alignment = args.alignment;
......@@ -319,7 +354,7 @@ fn initLiteralSections(self: *Object, macho_file: *MachO) !void {
319354 // TODO here we should split into equal-sized records, hash the contents, and then
320355 // deduplicate - ICF.
321356 // For now, we simply cover each literal section with one large atom.
322 const gpa = macho_file.base.allocator;
357 const gpa = macho_file.base.comp.gpa;
323358 const slice = self.sections.slice();
324359
325360 try self.atoms.ensureUnusedCapacity(gpa, self.sections.items(.header).len);
......@@ -401,10 +436,10 @@ fn linkNlistToAtom(self: *Object, macho_file: *MachO) !void {
401436 if (self.findAtomInSection(nlist.n_value, nlist.n_sect - 1)) |atom_index| {
402437 atom.* = atom_index;
403438 } else {
404 macho_file.base.fatal("{}: symbol {s} not attached to any (sub)section", .{
405 self.fmtPath(), self.getString(nlist.n_strx),
439 try macho_file.reportParseError2(self.index, "symbol {s} not attached to any (sub)section", .{
440 self.getString(nlist.n_strx),
406441 });
407 return error.ParseFailed;
442 return error.MalformedObject;
408443 }
409444 }
410445 }
......@@ -413,7 +448,7 @@ fn linkNlistToAtom(self: *Object, macho_file: *MachO) !void {
413448fn initSymbols(self: *Object, macho_file: *MachO) !void {
414449 const tracy = trace(@src());
415450 defer tracy.end();
416 const gpa = macho_file.base.allocator;
451 const gpa = macho_file.base.comp.gpa;
417452 const slice = self.symtab.slice();
418453
419454 try self.symbols.ensureUnusedCapacity(gpa, slice.items(.nlist).len);
......@@ -421,7 +456,7 @@ fn initSymbols(self: *Object, macho_file: *MachO) !void {
421456 for (slice.items(.nlist), slice.items(.atom), 0..) |nlist, atom_index, i| {
422457 if (nlist.ext()) {
423458 const name = self.getString(nlist.n_strx);
424 const off = try macho_file.string_intern.insert(gpa, name);
459 const off = try macho_file.strings.insert(gpa, name);
425460 const gop = try macho_file.getOrCreateGlobal(off);
426461 self.symbols.addOneAssumeCapacity().* = gop.index;
427462 continue;
......@@ -433,7 +468,7 @@ fn initSymbols(self: *Object, macho_file: *MachO) !void {
433468 const name = self.getString(nlist.n_strx);
434469 symbol.* = .{
435470 .value = nlist.n_value,
436 .name = try macho_file.string_intern.insert(gpa, name),
471 .name = try macho_file.strings.insert(gpa, name),
437472 .nlist_idx = @intCast(i),
438473 .atom = 0,
439474 .file = self.index,
......@@ -482,7 +517,7 @@ fn initSymbolStabs(self: *Object, nlists: anytype, macho_file: *MachO) !void {
482517
483518 if (start == end) return;
484519
485 const gpa = macho_file.base.allocator;
520 const gpa = macho_file.base.comp.gpa;
486521 const syms = self.symtab.items(.nlist);
487522 const sym_lookup = SymbolLookup{ .ctx = self, .entries = nlists };
488523
......@@ -490,11 +525,10 @@ fn initSymbolStabs(self: *Object, nlists: anytype, macho_file: *MachO) !void {
490525 while (i < end) : (i += 1) {
491526 const open = syms[i];
492527 if (open.n_type != macho.N_SO) {
493 macho_file.base.fatal("{}: unexpected symbol stab type 0x{x} as the first entry", .{
494 self.fmtPath(),
528 try macho_file.reportParseError2(self.index, "unexpected symbol stab type 0x{x} as the first entry", .{
495529 open.n_type,
496530 });
497 return error.ParseFailed;
531 return error.MalformedObject;
498532 }
499533
500534 while (i < end and syms[i].n_type == macho.N_SO and syms[i].n_sect != 0) : (i += 1) {}
......@@ -522,11 +556,10 @@ fn initSymbolStabs(self: *Object, nlists: anytype, macho_file: *MachO) !void {
522556 stab.symbol = sym_lookup.find(nlist.n_value);
523557 },
524558 else => {
525 macho_file.base.fatal("{}: unhandled symbol stab type 0x{x}", .{
526 self.fmtPath(),
559 try macho_file.reportParseError2(self.index, "unhandled symbol stab type 0x{x}", .{
527560 nlist.n_type,
528561 });
529 return error.ParseFailed;
562 return error.MalformedObject;
530563 },
531564 }
532565 try sf.stabs.append(gpa, stab);
......@@ -548,7 +581,7 @@ fn sortAtoms(self: *Object, macho_file: *MachO) !void {
548581fn initRelocs(self: *Object, macho_file: *MachO) !void {
549582 const tracy = trace(@src());
550583 defer tracy.end();
551 const cpu_arch = macho_file.options.cpu_arch.?;
584 const cpu_arch = macho_file.getTarget().cpu.arch;
552585 const slice = self.sections.slice();
553586
554587 for (slice.items(.header), slice.items(.relocs), 0..) |sect, *out, n_sect| {
......@@ -589,7 +622,7 @@ fn initRelocs(self: *Object, macho_file: *MachO) !void {
589622fn initEhFrameRecords(self: *Object, sect_id: u8, macho_file: *MachO) !void {
590623 const tracy = trace(@src());
591624 defer tracy.end();
592 const gpa = macho_file.base.allocator;
625 const gpa = macho_file.base.comp.gpa;
593626 const nlists = self.symtab.items(.nlist);
594627 const slice = self.sections.slice();
595628 const sect = slice.items(.header)[sect_id];
......@@ -667,10 +700,10 @@ fn initEhFrameRecords(self: *Object, sect_id: u8, macho_file: *MachO) !void {
667700 const cie = for (self.cies.items) |*cie| {
668701 if (cie.offset <= rel.offset and rel.offset < cie.offset + cie.getSize()) break cie;
669702 } else {
670 macho_file.base.fatal("{}: {s},{s}: 0x{x}: bad relocation", .{
671 self.fmtPath(), sect.segName(), sect.sectName(), rel.offset,
703 try macho_file.reportParseError2(self.index, "{s},{s}: 0x{x}: bad relocation", .{
704 sect.segName(), sect.sectName(), rel.offset,
672705 });
673 return error.ParseFailed;
706 return error.MalformedObject;
674707 };
675708 cie.personality = .{ .index = @intCast(rel.target), .offset = rel.offset - cie.offset };
676709 },
......@@ -695,7 +728,7 @@ fn initUnwindRecords(self: *Object, sect_id: u8, macho_file: *MachO) !void {
695728 }
696729 };
697730
698 const gpa = macho_file.base.allocator;
731 const gpa = macho_file.base.comp.gpa;
699732 const data = self.getSectionData(sect_id);
700733 const nrecs = @divExact(data.len, @sizeOf(macho.compact_unwind_entry));
701734 const recs = @as([*]align(1) const macho.compact_unwind_entry, @ptrCast(data.ptr))[0..nrecs];
......@@ -722,10 +755,10 @@ fn initUnwindRecords(self: *Object, sect_id: u8, macho_file: *MachO) !void {
722755
723756 for (relocs[reloc_start..reloc_idx]) |rel| {
724757 if (rel.type != .unsigned or rel.meta.length != 3) {
725 macho_file.base.fatal("{}: {s},{s}: 0x{x}: bad relocation", .{
726 self.fmtPath(), header.segName(), header.sectName(), rel.offset,
758 try macho_file.reportParseError2(self.index, "{s},{s}: 0x{x}: bad relocation", .{
759 header.segName(), header.sectName(), rel.offset,
727760 });
728 return error.ParseFailed;
761 return error.MalformedObject;
729762 }
730763 assert(rel.type == .unsigned and rel.meta.length == 3); // TODO error
731764 const offset = rel.offset - rec_start;
......@@ -740,10 +773,10 @@ fn initUnwindRecords(self: *Object, sect_id: u8, macho_file: *MachO) !void {
740773 const atom = out.getAtom(macho_file);
741774 out.atom_offset = @intCast(rec.rangeStart - atom.getInputAddress(macho_file));
742775 } else {
743 macho_file.base.fatal("{}: {s},{s}: 0x{x}: bad relocation", .{
744 self.fmtPath(), header.segName(), header.sectName(), rel.offset,
776 try macho_file.reportParseError2(self.index, "{s},{s}: 0x{x}: bad relocation", .{
777 header.segName(), header.sectName(), rel.offset,
745778 });
746 return error.ParseFailed;
779 return error.MalformedObject;
747780 },
748781 },
749782 16 => switch (rel.tag) { // personality function
......@@ -753,10 +786,10 @@ fn initUnwindRecords(self: *Object, sect_id: u8, macho_file: *MachO) !void {
753786 .local => if (sym_lookup.find(rec.personalityFunction)) |sym_index| {
754787 out.personality = sym_index;
755788 } else {
756 macho_file.base.fatal("{}: {s},{s}: 0x{x}: bad relocation", .{
757 self.fmtPath(), header.segName(), header.sectName(), rel.offset,
789 try macho_file.reportParseError2(self.index, "{s},{s}: 0x{x}: bad relocation", .{
790 header.segName(), header.sectName(), rel.offset,
758791 });
759 return error.ParseFailed;
792 return error.MalformedObject;
760793 },
761794 },
762795 24 => switch (rel.tag) { // lsda
......@@ -769,10 +802,10 @@ fn initUnwindRecords(self: *Object, sect_id: u8, macho_file: *MachO) !void {
769802 const atom = out.getLsdaAtom(macho_file).?;
770803 out.lsda_offset = @intCast(rec.lsda - atom.getInputAddress(macho_file));
771804 } else {
772 macho_file.base.fatal("{}: {s},{s}: 0x{x}: bad relocation", .{
773 self.fmtPath(), header.segName(), header.sectName(), rel.offset,
805 try macho_file.reportParseError2(self.index, "{s},{s}: 0x{x}: bad relocation", .{
806 header.segName(), header.sectName(), rel.offset,
774807 });
775 return error.ParseFailed;
808 return error.MalformedObject;
776809 },
777810 },
778811 else => {},
......@@ -780,7 +813,7 @@ fn initUnwindRecords(self: *Object, sect_id: u8, macho_file: *MachO) !void {
780813 }
781814 }
782815
783 if (!macho_file.options.relocatable) try self.synthesiseNullUnwindRecords(macho_file);
816 if (!macho_file.base.isObject()) try self.synthesiseNullUnwindRecords(macho_file);
784817
785818 const sortFn = struct {
786819 fn sortFn(ctx: *MachO, lhs_index: UnwindInfo.Record.Index, rhs_index: UnwindInfo.Record.Index) bool {
......@@ -818,7 +851,7 @@ fn synthesiseNullUnwindRecords(self: *Object, macho_file: *MachO) !void {
818851
819852 const Superposition = struct { atom: Atom.Index, size: u64, cu: ?UnwindInfo.Record.Index = null, fde: ?Fde.Index = null };
820853
821 const gpa = macho_file.base.allocator;
854 const gpa = macho_file.base.comp.gpa;
822855 var superposition = std.AutoArrayHashMap(u64, Superposition).init(gpa);
823856 defer superposition.deinit();
824857
......@@ -875,7 +908,7 @@ fn synthesiseNullUnwindRecords(self: *Object, macho_file: *MachO) !void {
875908 rec.atom_offset = fde.atom_offset;
876909 rec.fde = fde_index;
877910 rec.file = fde.file;
878 switch (macho_file.options.cpu_arch.?) {
911 switch (macho_file.getTarget().cpu.arch) {
879912 .x86_64 => rec.enc.setMode(macho.UNWIND_X86_64_MODE.DWARF),
880913 .aarch64 => rec.enc.setMode(macho.UNWIND_ARM64_MODE.DWARF),
881914 else => unreachable,
......@@ -907,7 +940,7 @@ fn initPlatform(self: *Object) void {
907940 .VERSION_MIN_IPHONEOS,
908941 .VERSION_MIN_TVOS,
909942 .VERSION_MIN_WATCHOS,
910 => break MachO.Options.Platform.fromLoadCommand(cmd),
943 => break MachO.Platform.fromLoadCommand(cmd),
911944 else => {},
912945 }
913946 } else null;
......@@ -921,7 +954,7 @@ fn initDwarfInfo(self: *Object, macho_file: *MachO) !void {
921954 const tracy = trace(@src());
922955 defer tracy.end();
923956
924 const gpa = macho_file.base.allocator;
957 const gpa = macho_file.base.comp.gpa;
925958
926959 var debug_info_index: ?usize = null;
927960 var debug_abbrev_index: ?usize = null;
......@@ -942,8 +975,8 @@ fn initDwarfInfo(self: *Object, macho_file: *MachO) !void {
942975 .debug_str = if (debug_str_index) |index| self.getSectionData(@intCast(index)) else "",
943976 };
944977 dwarf_info.init(gpa) catch {
945 macho_file.base.fatal("{}: invalid __DWARF info found", .{self.fmtPath()});
946 return error.ParseFailed;
978 try macho_file.reportParseError2(self.index, "invalid __DWARF info found", .{});
979 return error.MalformedObject;
947980 };
948981 self.dwarf_info = dwarf_info;
949982}
......@@ -1060,7 +1093,7 @@ pub fn scanRelocs(self: Object, macho_file: *MachO) !void {
10601093pub fn convertTentativeDefinitions(self: *Object, macho_file: *MachO) !void {
10611094 const tracy = trace(@src());
10621095 defer tracy.end();
1063 const gpa = macho_file.base.allocator;
1096 const gpa = macho_file.base.comp.gpa;
10641097
10651098 for (self.symbols.items, 0..) |index, i| {
10661099 const sym = macho_file.getSymbol(index);
......@@ -1079,7 +1112,7 @@ pub fn convertTentativeDefinitions(self: *Object, macho_file: *MachO) !void {
10791112 defer gpa.free(name);
10801113 const atom = macho_file.getAtom(atom_index).?;
10811114 atom.atom_index = atom_index;
1082 atom.name = try macho_file.string_intern.insert(gpa, name);
1115 atom.name = try macho_file.strings.insert(gpa, name);
10831116 atom.file = self.index;
10841117 atom.size = nlist.n_value;
10851118 atom.alignment = (nlist.n_desc >> 8) & 0x0f;
......@@ -1130,7 +1163,7 @@ pub fn calcSymtabSize(self: *Object, macho_file: *MachO) !void {
11301163 const name = sym.getName(macho_file);
11311164 // TODO in -r mode, we actually want to merge symbol names and emit only one
11321165 // work it out when emitting relocs
1133 if (name.len > 0 and (name[0] == 'L' or name[0] == 'l') and !macho_file.options.relocatable) continue;
1166 if (name.len > 0 and (name[0] == 'L' or name[0] == 'l') and !macho_file.base.isObject()) continue;
11341167 sym.flags.output_symtab = true;
11351168 if (sym.isLocal()) {
11361169 try sym.addExtra(.{ .symtab = self.output_symtab_ctx.nlocals }, macho_file);
......@@ -1171,7 +1204,7 @@ pub fn calcStabsSize(self: *Object, macho_file: *MachO) void {
11711204 const file = sym.getFile(macho_file) orelse continue;
11721205 if (file.getIndex() != self.index) continue;
11731206 if (!sym.flags.output_symtab) continue;
1174 if (macho_file.options.relocatable) {
1207 if (macho_file.base.isObject()) {
11751208 const name = sym.getName(macho_file);
11761209 if (name.len > 0 and (name[0] == 'L' or name[0] == 'l')) continue;
11771210 }
......@@ -1329,7 +1362,7 @@ pub fn writeStabs(self: *const Object, macho_file: *MachO) void {
13291362 const file = sym.getFile(macho_file) orelse continue;
13301363 if (file.getIndex() != self.index) continue;
13311364 if (!sym.flags.output_symtab) continue;
1332 if (macho_file.options.relocatable) {
1365 if (macho_file.base.isObject()) {
13331366 const name = sym.getName(macho_file);
13341367 if (name.len > 0 and (name[0] == 'L' or name[0] == 'l')) continue;
13351368 }
......@@ -1747,7 +1780,7 @@ const x86_64 = struct {
17471780 out: *std.ArrayListUnmanaged(Relocation),
17481781 macho_file: *MachO,
17491782 ) !void {
1750 const gpa = macho_file.base.allocator;
1783 const gpa = macho_file.base.comp.gpa;
17511784
17521785 const relocs = @as(
17531786 [*]align(1) const macho.relocation_info,
......@@ -1783,10 +1816,10 @@ const x86_64 = struct {
17831816 else
17841817 addend;
17851818 const target = self.findAtomInSection(@intCast(taddr), @intCast(nsect)) orelse {
1786 macho_file.base.fatal("{}: {s},{s}: 0x{x}: bad relocation", .{
1787 self.fmtPath(), sect.segName(), sect.sectName(), rel.r_address,
1819 try macho_file.reportParseError2(self.index, "{s},{s}: 0x{x}: bad relocation", .{
1820 sect.segName(), sect.sectName(), rel.r_address,
17881821 });
1789 return error.ParseFailed;
1822 return error.MalformedObject;
17901823 };
17911824 addend = taddr - @as(i64, @intCast(macho_file.getAtom(target).?.getInputAddress(macho_file)));
17921825 break :blk target;
......@@ -1796,34 +1829,38 @@ const x86_64 = struct {
17961829 @as(macho.reloc_type_x86_64, @enumFromInt(relocs[i - 1].r_type)) == .X86_64_RELOC_SUBTRACTOR)
17971830 blk: {
17981831 if (rel_type != .X86_64_RELOC_UNSIGNED) {
1799 macho_file.base.fatal("{}: {s},{s}: 0x{x}: X86_64_RELOC_SUBTRACTOR followed by {s}", .{
1800 self.fmtPath(), sect.segName(), sect.sectName(), rel_offset, @tagName(rel_type),
1832 try macho_file.reportParseError2(self.index, "{s},{s}: 0x{x}: X86_64_RELOC_SUBTRACTOR followed by {s}", .{
1833 sect.segName(), sect.sectName(), rel_offset, @tagName(rel_type),
18011834 });
1802 return error.ParseFailed;
1835 return error.MalformedObject;
18031836 }
18041837 break :blk true;
18051838 } else false;
18061839
18071840 const @"type": Relocation.Type = validateRelocType(rel, rel_type) catch |err| {
18081841 switch (err) {
1809 error.Pcrel => macho_file.base.fatal(
1810 "{}: {s},{s}: 0x{x}: PC-relative {s} relocation",
1811 .{ self.fmtPath(), sect.segName(), sect.sectName(), rel_offset, @tagName(rel_type) },
1842 error.Pcrel => try macho_file.reportParseError2(
1843 self.index,
1844 "{s},{s}: 0x{x}: PC-relative {s} relocation",
1845 .{ sect.segName(), sect.sectName(), rel_offset, @tagName(rel_type) },
18121846 ),
1813 error.NonPcrel => macho_file.base.fatal(
1814 "{}: {s},{s}: 0x{x}: non-PC-relative {s} relocation",
1815 .{ self.fmtPath(), sect.segName(), sect.sectName(), rel_offset, @tagName(rel_type) },
1847 error.NonPcrel => try macho_file.reportParseError2(
1848 self.index,
1849 "{s},{s}: 0x{x}: non-PC-relative {s} relocation",
1850 .{ sect.segName(), sect.sectName(), rel_offset, @tagName(rel_type) },
18161851 ),
1817 error.InvalidLength => macho_file.base.fatal(
1818 "{}: {s},{s}: 0x{x}: invalid length of {d} in {s} relocation",
1819 .{ self.fmtPath(), sect.segName(), sect.sectName(), rel_offset, @as(u8, 1) << rel.r_length, @tagName(rel_type) },
1852 error.InvalidLength => try macho_file.reportParseError2(
1853 self.index,
1854 "{s},{s}: 0x{x}: invalid length of {d} in {s} relocation",
1855 .{ sect.segName(), sect.sectName(), rel_offset, @as(u8, 1) << rel.r_length, @tagName(rel_type) },
18201856 ),
1821 error.NonExtern => macho_file.base.fatal(
1822 "{}: {s},{s}: 0x{x}: non-extern target in {s} relocation",
1823 .{ self.fmtPath(), sect.segName(), sect.sectName(), rel_offset, @tagName(rel_type) },
1857 error.NonExtern => try macho_file.reportParseError2(
1858 self.index,
1859 "{s},{s}: 0x{x}: non-extern target in {s} relocation",
1860 .{ sect.segName(), sect.sectName(), rel_offset, @tagName(rel_type) },
18241861 ),
18251862 }
1826 return error.ParseFailed;
1863 return error.MalformedObject;
18271864 };
18281865
18291866 out.appendAssumeCapacity(.{
......@@ -1899,7 +1936,7 @@ const aarch64 = struct {
18991936 out: *std.ArrayListUnmanaged(Relocation),
19001937 macho_file: *MachO,
19011938 ) !void {
1902 const gpa = macho_file.base.allocator;
1939 const gpa = macho_file.base.comp.gpa;
19031940
19041941 const relocs = @as(
19051942 [*]align(1) const macho.relocation_info,
......@@ -1921,20 +1958,21 @@ const aarch64 = struct {
19211958 addend = rel.r_symbolnum;
19221959 i += 1;
19231960 if (i >= relocs.len) {
1924 macho_file.base.fatal("{}: {s},{s}: 0x{x}: unterminated ARM64_RELOC_ADDEND", .{
1925 self.fmtPath(), sect.segName(), sect.sectName(), rel_offset,
1961 try macho_file.reportParseError2(self.index, "{s},{s}: 0x{x}: unterminated ARM64_RELOC_ADDEND", .{
1962 sect.segName(), sect.sectName(), rel_offset,
19261963 });
1927 return error.ParseFailed;
1964 return error.MalformedObject;
19281965 }
19291966 rel = relocs[i];
19301967 switch (@as(macho.reloc_type_arm64, @enumFromInt(rel.r_type))) {
19311968 .ARM64_RELOC_PAGE21, .ARM64_RELOC_PAGEOFF12 => {},
19321969 else => |x| {
1933 macho_file.base.fatal(
1934 "{}: {s},{s}: 0x{x}: ARM64_RELOC_ADDEND followed by {s}",
1935 .{ self.fmtPath(), sect.segName(), sect.sectName(), rel_offset, @tagName(x) },
1970 try macho_file.reportParseError2(
1971 self.index,
1972 "{s},{s}: 0x{x}: ARM64_RELOC_ADDEND followed by {s}",
1973 .{ sect.segName(), sect.sectName(), rel_offset, @tagName(x) },
19361974 );
1937 return error.ParseFailed;
1975 return error.MalformedObject;
19381976 },
19391977 }
19401978 },
......@@ -1958,10 +1996,10 @@ const aarch64 = struct {
19581996 else
19591997 addend;
19601998 const target = self.findAtomInSection(@intCast(taddr), @intCast(nsect)) orelse {
1961 macho_file.base.fatal("{}: {s},{s}: 0x{x}: bad relocation", .{
1962 self.fmtPath(), sect.segName(), sect.sectName(), rel.r_address,
1999 try macho_file.reportParseError2(self.index, "{s},{s}: 0x{x}: bad relocation", .{
2000 sect.segName(), sect.sectName(), rel.r_address,
19632001 });
1964 return error.ParseFailed;
2002 return error.MalformedObject;
19652003 };
19662004 addend = taddr - @as(i64, @intCast(macho_file.getAtom(target).?.getInputAddress(macho_file)));
19672005 break :blk target;
......@@ -1971,34 +2009,38 @@ const aarch64 = struct {
19712009 @as(macho.reloc_type_arm64, @enumFromInt(relocs[i - 1].r_type)) == .ARM64_RELOC_SUBTRACTOR)
19722010 blk: {
19732011 if (rel_type != .ARM64_RELOC_UNSIGNED) {
1974 macho_file.base.fatal("{}: {s},{s}: 0x{x}: ARM64_RELOC_SUBTRACTOR followed by {s}", .{
1975 self.fmtPath(), sect.segName(), sect.sectName(), rel_offset, @tagName(rel_type),
2012 try macho_file.reportParseError2(self.index, "{s},{s}: 0x{x}: ARM64_RELOC_SUBTRACTOR followed by {s}", .{
2013 sect.segName(), sect.sectName(), rel_offset, @tagName(rel_type),
19762014 });
1977 return error.ParseFailed;
2015 return error.MalformedObject;
19782016 }
19792017 break :blk true;
19802018 } else false;
19812019
19822020 const @"type": Relocation.Type = validateRelocType(rel, rel_type) catch |err| {
19832021 switch (err) {
1984 error.Pcrel => macho_file.base.fatal(
1985 "{}: {s},{s}: 0x{x}: PC-relative {s} relocation",
1986 .{ self.fmtPath(), sect.segName(), sect.sectName(), rel_offset, @tagName(rel_type) },
2022 error.Pcrel => try macho_file.reportParseError2(
2023 self.index,
2024 "{s},{s}: 0x{x}: PC-relative {s} relocation",
2025 .{ sect.segName(), sect.sectName(), rel_offset, @tagName(rel_type) },
19872026 ),
1988 error.NonPcrel => macho_file.base.fatal(
1989 "{}: {s},{s}: 0x{x}: non-PC-relative {s} relocation",
1990 .{ self.fmtPath(), sect.segName(), sect.sectName(), rel_offset, @tagName(rel_type) },
2027 error.NonPcrel => try macho_file.reportParseError2(
2028 self.index,
2029 "{s},{s}: 0x{x}: non-PC-relative {s} relocation",
2030 .{ sect.segName(), sect.sectName(), rel_offset, @tagName(rel_type) },
19912031 ),
1992 error.InvalidLength => macho_file.base.fatal(
1993 "{}: {s},{s}: 0x{x}: invalid length of {d} in {s} relocation",
1994 .{ self.fmtPath(), sect.segName(), sect.sectName(), rel_offset, @as(u8, 1) << rel.r_length, @tagName(rel_type) },
2032 error.InvalidLength => try macho_file.reportParseError2(
2033 self.index,
2034 "{s},{s}: 0x{x}: invalid length of {d} in {s} relocation",
2035 .{ sect.segName(), sect.sectName(), rel_offset, @as(u8, 1) << rel.r_length, @tagName(rel_type) },
19952036 ),
1996 error.NonExtern => macho_file.base.fatal(
1997 "{}: {s},{s}: 0x{x}: non-extern target in {s} relocation",
1998 .{ self.fmtPath(), sect.segName(), sect.sectName(), rel_offset, @tagName(rel_type) },
2037 error.NonExtern => try macho_file.reportParseError2(
2038 self.index,
2039 "{s},{s}: 0x{x}: non-extern target in {s} relocation",
2040 .{ sect.segName(), sect.sectName(), rel_offset, @tagName(rel_type) },
19992041 ),
20002042 }
2001 return error.ParseFailed;
2043 return error.MalformedObject;
20022044 };
20032045
20042046 out.appendAssumeCapacity(.{
src/link/MachO/Symbol.zig+1-1
......@@ -55,7 +55,7 @@ pub fn weakRef(symbol: Symbol, macho_file: *MachO) bool {
5555}
5656
5757pub fn getName(symbol: Symbol, macho_file: *MachO) [:0]const u8 {
58 return macho_file.string_intern.getAssumeExists(symbol.name);
58 return macho_file.strings.getAssumeExists(symbol.name);
5959}
6060
6161pub fn getAtom(symbol: Symbol, macho_file: *MachO) ?*Atom {
src/link/MachO/UnwindInfo.zig+1-1
......@@ -372,7 +372,7 @@ pub const Encoding = extern struct {
372372
373373 pub fn isDwarf(enc: Encoding, macho_file: *MachO) bool {
374374 const mode = enc.getMode();
375 return switch (macho_file.options.cpu_arch.?) {
375 return switch (macho_file.getTarget().cpu.arch) {
376376 .aarch64 => @as(macho.UNWIND_ARM64_MODE, @enumFromInt(mode)) == .DWARF,
377377 .x86_64 => @as(macho.UNWIND_X86_64_MODE, @enumFromInt(mode)) == .DWARF,
378378 else => unreachable,
src/link/MachO/eh_frame.zig+8-9
......@@ -155,10 +155,10 @@ pub const Fde = struct {
155155 const pc_begin = std.mem.readInt(i64, data[8..][0..8], .little);
156156 const taddr: u64 = @intCast(@as(i64, @intCast(sect.addr + fde.offset + 8)) + pc_begin);
157157 fde.atom = object.findAtom(taddr) orelse {
158 macho_file.base.fatal("{}: {s},{s}: 0x{x}: invalid function reference in FDE", .{
159 object.fmtPath(), sect.segName(), sect.sectName(), fde.offset + 8,
158 try macho_file.reportParseError2(object.index, "{s},{s}: 0x{x}: invalid function reference in FDE", .{
159 sect.segName(), sect.sectName(), fde.offset + 8,
160160 });
161 return error.ParseFailed;
161 return error.MalformedObject;
162162 };
163163 const atom = fde.getAtom(macho_file);
164164 fde.atom_offset = @intCast(taddr - atom.getInputAddress(macho_file));
......@@ -172,11 +172,10 @@ pub const Fde = struct {
172172 if (cie_index) |cie| {
173173 fde.cie = cie;
174174 } else {
175 macho_file.base.fatal("{}: no matching CIE found for FDE at offset {x}", .{
176 object.fmtPath(),
175 try macho_file.reportParseError2(object.index, "no matching CIE found for FDE at offset {x}", .{
177176 fde.offset,
178177 });
179 return error.ParseFailed;
178 return error.MalformedObject;
180179 }
181180
182181 const cie = fde.getCie(macho_file);
......@@ -194,10 +193,10 @@ pub const Fde = struct {
194193 };
195194 const lsda_addr: u64 = @intCast(@as(i64, @intCast(sect.addr + fde.offset + fde.lsda_ptr_offset)) + lsda_ptr);
196195 fde.lsda = object.findAtom(lsda_addr) orelse {
197 macho_file.base.fatal("{}: {s},{s}: 0x{x}: invalid LSDA reference in FDE", .{
198 object.fmtPath(), sect.segName(), sect.sectName(), fde.offset + fde.lsda_ptr_offset,
196 try macho_file.reportParseError2(object.index, "{s},{s}: 0x{x}: invalid LSDA reference in FDE", .{
197 sect.segName(), sect.sectName(), fde.offset + fde.lsda_ptr_offset,
199198 });
200 return error.ParseFailed;
199 return error.MalformedObject;
201200 };
202201 const lsda_atom = fde.getLsdaAtom(macho_file).?;
203202 fde.lsda_offset = @intCast(lsda_addr - lsda_atom.getInputAddress(macho_file));
src/link/MachO/synthetic.zig+17-17
......@@ -8,7 +8,7 @@ pub const GotSection = struct {
88 }
99
1010 pub fn addSymbol(got: *GotSection, sym_index: Symbol.Index, macho_file: *MachO) !void {
11 const gpa = macho_file.base.allocator;
11 const gpa = macho_file.base.comp.gpa;
1212 const index = @as(Index, @intCast(got.symbols.items.len));
1313 const entry = try got.symbols.addOne(gpa);
1414 entry.* = sym_index;
......@@ -29,7 +29,7 @@ pub const GotSection = struct {
2929 pub fn addDyldRelocs(got: GotSection, macho_file: *MachO) !void {
3030 const tracy = trace(@src());
3131 defer tracy.end();
32 const gpa = macho_file.base.allocator;
32 const gpa = macho_file.base.comp.gpa;
3333 const seg_id = macho_file.sections.items(.segment_id)[macho_file.got_sect_index.?];
3434 const seg = macho_file.segments.items[seg_id];
3535
......@@ -111,7 +111,7 @@ pub const StubsSection = struct {
111111 }
112112
113113 pub fn addSymbol(stubs: *StubsSection, sym_index: Symbol.Index, macho_file: *MachO) !void {
114 const gpa = macho_file.base.allocator;
114 const gpa = macho_file.base.comp.gpa;
115115 const index = @as(Index, @intCast(stubs.symbols.items.len));
116116 const entry = try stubs.symbols.addOne(gpa);
117117 entry.* = sym_index;
......@@ -133,7 +133,7 @@ pub const StubsSection = struct {
133133 pub fn write(stubs: StubsSection, macho_file: *MachO, writer: anytype) !void {
134134 const tracy = trace(@src());
135135 defer tracy.end();
136 const cpu_arch = macho_file.options.cpu_arch.?;
136 const cpu_arch = macho_file.getTarget().cpu.arch;
137137 const laptr_sect = macho_file.sections.items(.header)[macho_file.la_symbol_ptr_sect_index.?];
138138
139139 for (stubs.symbols.items, 0..) |sym_index, idx| {
......@@ -213,7 +213,7 @@ pub const StubsHelperSection = struct {
213213 const tracy = trace(@src());
214214 defer tracy.end();
215215 _ = stubs_helper;
216 const cpu_arch = macho_file.options.cpu_arch.?;
216 const cpu_arch = macho_file.getTarget().cpu.arch;
217217 var s: usize = preambleSize(cpu_arch);
218218 for (macho_file.stubs.symbols.items) |sym_index| {
219219 const sym = macho_file.getSymbol(sym_index);
......@@ -230,7 +230,7 @@ pub const StubsHelperSection = struct {
230230
231231 try stubs_helper.writePreamble(macho_file, writer);
232232
233 const cpu_arch = macho_file.options.cpu_arch.?;
233 const cpu_arch = macho_file.getTarget().cpu.arch;
234234 const sect = macho_file.sections.items(.header)[macho_file.stubs_helper_sect_index.?];
235235 const preamble_size = preambleSize(cpu_arch);
236236 const entry_size = entrySize(cpu_arch);
......@@ -272,7 +272,7 @@ pub const StubsHelperSection = struct {
272272
273273 fn writePreamble(stubs_helper: StubsHelperSection, macho_file: *MachO, writer: anytype) !void {
274274 _ = stubs_helper;
275 const cpu_arch = macho_file.options.cpu_arch.?;
275 const cpu_arch = macho_file.getTarget().cpu.arch;
276276 const sect = macho_file.sections.items(.header)[macho_file.stubs_helper_sect_index.?];
277277 const dyld_private_addr = target: {
278278 const sym = macho_file.getSymbol(macho_file.dyld_private_index.?);
......@@ -331,7 +331,7 @@ pub const LaSymbolPtrSection = struct {
331331 const tracy = trace(@src());
332332 defer tracy.end();
333333 _ = laptr;
334 const gpa = macho_file.base.allocator;
334 const gpa = macho_file.base.comp.gpa;
335335
336336 const sect = macho_file.sections.items(.header)[macho_file.la_symbol_ptr_sect_index.?];
337337 const seg_id = macho_file.sections.items(.segment_id)[macho_file.la_symbol_ptr_sect_index.?];
......@@ -371,7 +371,7 @@ pub const LaSymbolPtrSection = struct {
371371 const tracy = trace(@src());
372372 defer tracy.end();
373373 _ = laptr;
374 const cpu_arch = macho_file.options.cpu_arch.?;
374 const cpu_arch = macho_file.getTarget().cpu.arch;
375375 const sect = macho_file.sections.items(.header)[macho_file.stubs_helper_sect_index.?];
376376 for (macho_file.stubs.symbols.items, 0..) |sym_index, idx| {
377377 const sym = macho_file.getSymbol(sym_index);
......@@ -397,7 +397,7 @@ pub const TlvPtrSection = struct {
397397 }
398398
399399 pub fn addSymbol(tlv: *TlvPtrSection, sym_index: Symbol.Index, macho_file: *MachO) !void {
400 const gpa = macho_file.base.allocator;
400 const gpa = macho_file.base.comp.gpa;
401401 const index = @as(Index, @intCast(tlv.symbols.items.len));
402402 const entry = try tlv.symbols.addOne(gpa);
403403 entry.* = sym_index;
......@@ -418,7 +418,7 @@ pub const TlvPtrSection = struct {
418418 pub fn addDyldRelocs(tlv: TlvPtrSection, macho_file: *MachO) !void {
419419 const tracy = trace(@src());
420420 defer tracy.end();
421 const gpa = macho_file.base.allocator;
421 const gpa = macho_file.base.comp.gpa;
422422 const seg_id = macho_file.sections.items(.segment_id)[macho_file.tlv_ptr_sect_index.?];
423423 const seg = macho_file.segments.items[seg_id];
424424
......@@ -510,7 +510,7 @@ pub const ObjcStubsSection = struct {
510510 }
511511
512512 pub fn addSymbol(objc: *ObjcStubsSection, sym_index: Symbol.Index, macho_file: *MachO) !void {
513 const gpa = macho_file.base.allocator;
513 const gpa = macho_file.base.comp.gpa;
514514 const index = @as(Index, @intCast(objc.symbols.items.len));
515515 const entry = try objc.symbols.addOne(gpa);
516516 entry.* = sym_index;
......@@ -521,11 +521,11 @@ pub const ObjcStubsSection = struct {
521521 pub fn getAddress(objc: ObjcStubsSection, index: Index, macho_file: *MachO) u64 {
522522 assert(index < objc.symbols.items.len);
523523 const header = macho_file.sections.items(.header)[macho_file.objc_stubs_sect_index.?];
524 return header.addr + index * entrySize(macho_file.options.cpu_arch.?);
524 return header.addr + index * entrySize(macho_file.getTarget().cpu.arch);
525525 }
526526
527527 pub fn size(objc: ObjcStubsSection, macho_file: *MachO) usize {
528 return objc.symbols.items.len * entrySize(macho_file.options.cpu_arch.?);
528 return objc.symbols.items.len * entrySize(macho_file.getTarget().cpu.arch);
529529 }
530530
531531 pub fn write(objc: ObjcStubsSection, macho_file: *MachO, writer: anytype) !void {
......@@ -535,7 +535,7 @@ pub const ObjcStubsSection = struct {
535535 for (objc.symbols.items, 0..) |sym_index, idx| {
536536 const sym = macho_file.getSymbol(sym_index);
537537 const addr = objc.getAddress(@intCast(idx), macho_file);
538 switch (macho_file.options.cpu_arch.?) {
538 switch (macho_file.getTarget().cpu.arch) {
539539 .x86_64 => {
540540 try writer.writeAll(&.{ 0x48, 0x8b, 0x35 });
541541 {
......@@ -654,12 +654,12 @@ pub const WeakBindSection = bind.WeakBind;
654654pub const LazyBindSection = bind.LazyBind;
655655pub const ExportTrieSection = Trie;
656656
657const aarch64 = @import("../aarch64.zig");
657const aarch64 = @import("../../arch/aarch64/bits.zig");
658658const assert = std.debug.assert;
659659const bind = @import("dyld_info/bind.zig");
660660const math = std.math;
661661const std = @import("std");
662const trace = @import("../tracy.zig").trace;
662const trace = @import("../../tracy.zig").trace;
663663
664664const Allocator = std.mem.Allocator;
665665const MachO = @import("../MachO.zig");