authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2024-07-18 22:23:30+02:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2024-07-18 22:23:30+02:00
log16604a93b9159fafec3528457366ca146bf29ce5
treec502d73aae538298e45f5703d43a7a96a73afe11
parente4f5dada615c103bfe656c9fcd8f28df5f5ef9d1
parentf5a941b3d6238222f43da568c96cfa5b3e56ce5d
signaturebadge-check Signed by PGP key B5690EEEBB952194

Merge pull request #20650 from ziglang/parallel-macho

The tale of parallel MachO: part 1

28 files changed, 4696 insertions(+), 3356 deletions(-)

build.zig+1-1
...@@ -620,7 +620,7 @@ fn addCompilerStep(b: *std.Build, options: AddCompilerStepOptions) *std.Build.St...@@ -620,7 +620,7 @@ fn addCompilerStep(b: *std.Build, options: AddCompilerStepOptions) *std.Build.St
620 .root_source_file = b.path("src/main.zig"),620 .root_source_file = b.path("src/main.zig"),
621 .target = options.target,621 .target = options.target,
622 .optimize = options.optimize,622 .optimize = options.optimize,
623 .max_rss = 7_000_000_000,623 .max_rss = 7_100_000_000,
624 .strip = options.strip,624 .strip = options.strip,
625 .sanitize_thread = options.sanitize_thread,625 .sanitize_thread = options.sanitize_thread,
626 .single_threaded = options.single_threaded,626 .single_threaded = options.single_threaded,
src/Sema.zig+1-2
...@@ -17787,8 +17787,7 @@ fn zirBuiltinSrc(...@@ -17787,8 +17787,7 @@ fn zirBuiltinSrc(
17787 };17787 };
1778817788
17789 const file_name_val = v: {17789 const file_name_val = v: {
17790 // The compiler must not call realpath anywhere.17790 const file_name = fn_owner_decl.getFileScope(mod).sub_file_path;
17791 const file_name = try fn_owner_decl.getFileScope(mod).fullPath(sema.arena);
17792 const array_ty = try pt.intern(.{ .array_type = .{17791 const array_ty = try pt.intern(.{ .array_type = .{
17793 .len = file_name.len,17792 .len = file_name.len,
17794 .sentinel = .zero_u8,17793 .sentinel = .zero_u8,
src/arch/x86_64/CodeGen.zig+6-4
...@@ -12362,8 +12362,9 @@ fn genCall(self: *Self, info: union(enum) {...@@ -12362,8 +12362,9 @@ fn genCall(self: *Self, info: union(enum) {
12362 try self.genSetReg(.rax, Type.usize, .{ .lea_got = sym_index }, .{});12362 try self.genSetReg(.rax, Type.usize, .{ .lea_got = sym_index }, .{});
12363 try self.asmRegister(.{ ._, .call }, .rax);12363 try self.asmRegister(.{ ._, .call }, .rax);
12364 } else if (self.bin_file.cast(link.File.MachO)) |macho_file| {12364 } else if (self.bin_file.cast(link.File.MachO)) |macho_file| {
12365 const sym_index = try macho_file.getZigObject().?.getOrCreateMetadataForDecl(macho_file, func.owner_decl);12365 const zo = macho_file.getZigObject().?;
12366 const sym = macho_file.getSymbol(sym_index);12366 const sym_index = try zo.getOrCreateMetadataForDecl(macho_file, func.owner_decl);
12367 const sym = zo.symbols.items[sym_index];
12367 try self.genSetReg(12368 try self.genSetReg(
12368 .rax,12369 .rax,
12369 Type.usize,12370 Type.usize,
...@@ -15396,9 +15397,10 @@ fn genLazySymbolRef(...@@ -15396,9 +15397,10 @@ fn genLazySymbolRef(
15396 else => unreachable,15397 else => unreachable,
15397 }15398 }
15398 } else if (self.bin_file.cast(link.File.MachO)) |macho_file| {15399 } else if (self.bin_file.cast(link.File.MachO)) |macho_file| {
15399 const sym_index = macho_file.getZigObject().?.getOrCreateMetadataForLazySymbol(macho_file, pt, lazy_sym) catch |err|15400 const zo = macho_file.getZigObject().?;
15401 const sym_index = zo.getOrCreateMetadataForLazySymbol(macho_file, pt, lazy_sym) catch |err|
15400 return self.fail("{s} creating lazy symbol", .{@errorName(err)});15402 return self.fail("{s} creating lazy symbol", .{@errorName(err)});
15401 const sym = macho_file.getSymbol(sym_index);15403 const sym = zo.symbols.items[sym_index];
15402 switch (tag) {15404 switch (tag) {
15403 .lea, .call => try self.genSetReg(15405 .lea, .call => try self.genSetReg(
15404 reg,15406 reg,
src/arch/x86_64/Emit.zig+8-8
...@@ -51,12 +51,12 @@ pub fn emitMir(emit: *Emit) Error!void {...@@ -51,12 +51,12 @@ pub fn emitMir(emit: *Emit) Error!void {
51 });51 });
52 } else if (emit.lower.bin_file.cast(link.File.MachO)) |macho_file| {52 } else if (emit.lower.bin_file.cast(link.File.MachO)) |macho_file| {
53 // Add relocation to the decl.53 // Add relocation to the decl.
54 const atom = macho_file.getSymbol(symbol.atom_index).getAtom(macho_file).?;54 const zo = macho_file.getZigObject().?;
55 const sym_index = macho_file.getZigObject().?.symbols.items[symbol.sym_index];55 const atom = zo.symbols.items[symbol.atom_index].getAtom(macho_file).?;
56 try atom.addReloc(macho_file, .{56 try atom.addReloc(macho_file, .{
57 .tag = .@"extern",57 .tag = .@"extern",
58 .offset = end_offset - 4,58 .offset = end_offset - 4,
59 .target = sym_index,59 .target = symbol.sym_index,
60 .addend = 0,60 .addend = 0,
61 .type = .branch,61 .type = .branch,
62 .meta = .{62 .meta = .{
...@@ -160,11 +160,11 @@ pub fn emitMir(emit: *Emit) Error!void {...@@ -160,11 +160,11 @@ pub fn emitMir(emit: *Emit) Error!void {
160 .Obj => true,160 .Obj => true,
161 .Lib => emit.lower.link_mode == .static,161 .Lib => emit.lower.link_mode == .static,
162 };162 };
163 const atom = macho_file.getSymbol(data.atom_index).getAtom(macho_file).?;163 const zo = macho_file.getZigObject().?;
164 const sym_index = macho_file.getZigObject().?.symbols.items[data.sym_index];164 const atom = zo.symbols.items[data.atom_index].getAtom(macho_file).?;
165 const sym = macho_file.getSymbol(sym_index);165 const sym = &zo.symbols.items[data.sym_index];
166 if (sym.flags.needs_zig_got and !is_obj_or_static_lib) {166 if (sym.flags.needs_zig_got and !is_obj_or_static_lib) {
167 _ = try sym.getOrCreateZigGotEntry(sym_index, macho_file);167 _ = try sym.getOrCreateZigGotEntry(data.sym_index, macho_file);
168 }168 }
169 const @"type": link.File.MachO.Relocation.Type = if (sym.flags.needs_zig_got and !is_obj_or_static_lib)169 const @"type": link.File.MachO.Relocation.Type = if (sym.flags.needs_zig_got and !is_obj_or_static_lib)
170 .zig_got_load170 .zig_got_load
...@@ -179,7 +179,7 @@ pub fn emitMir(emit: *Emit) Error!void {...@@ -179,7 +179,7 @@ pub fn emitMir(emit: *Emit) Error!void {
179 try atom.addReloc(macho_file, .{179 try atom.addReloc(macho_file, .{
180 .tag = .@"extern",180 .tag = .@"extern",
181 .offset = @intCast(end_offset - 4),181 .offset = @intCast(end_offset - 4),
182 .target = sym_index,182 .target = data.sym_index,
183 .addend = 0,183 .addend = 0,
184 .type = @"type",184 .type = @"type",
185 .meta = .{185 .meta = .{
src/arch/x86_64/Lower.zig+2-2
...@@ -425,8 +425,8 @@ fn emit(lower: *Lower, prefix: Prefix, mnemonic: Mnemonic, ops: []const Operand)...@@ -425,8 +425,8 @@ fn emit(lower: *Lower, prefix: Prefix, mnemonic: Mnemonic, ops: []const Operand)
425 else => unreachable,425 else => unreachable,
426 };426 };
427 } else if (lower.bin_file.cast(link.File.MachO)) |macho_file| {427 } else if (lower.bin_file.cast(link.File.MachO)) |macho_file| {
428 const sym_index = macho_file.getZigObject().?.symbols.items[sym.sym_index];428 const zo = macho_file.getZigObject().?;
429 const macho_sym = macho_file.getSymbol(sym_index);429 const macho_sym = zo.symbols.items[sym.sym_index];
430430
431 if (macho_sym.flags.tlv) {431 if (macho_sym.flags.tlv) {
432 _ = lower.reloc(.{ .linker_reloc = sym });432 _ = lower.reloc(.{ .linker_reloc = sym });
src/codegen.zig+5-4
...@@ -901,15 +901,16 @@ fn genDeclRef(...@@ -901,15 +901,16 @@ fn genDeclRef(
901 }901 }
902 return GenResult.mcv(.{ .load_symbol = sym.esym_index });902 return GenResult.mcv(.{ .load_symbol = sym.esym_index });
903 } else if (lf.cast(link.File.MachO)) |macho_file| {903 } else if (lf.cast(link.File.MachO)) |macho_file| {
904 const zo = macho_file.getZigObject().?;
904 if (is_extern) {905 if (is_extern) {
905 const name = decl.name.toSlice(ip);906 const name = decl.name.toSlice(ip);
906 const lib_name = if (decl.getOwnedVariable(zcu)) |ov| ov.lib_name.toSlice(ip) else null;907 const lib_name = if (decl.getOwnedVariable(zcu)) |ov| ov.lib_name.toSlice(ip) else null;
907 const sym_index = try macho_file.getGlobalSymbol(name, lib_name);908 const sym_index = try macho_file.getGlobalSymbol(name, lib_name);
908 macho_file.getSymbol(macho_file.getZigObject().?.symbols.items[sym_index]).flags.needs_got = true;909 zo.symbols.items[sym_index].flags.needs_got = true;
909 return GenResult.mcv(.{ .load_symbol = sym_index });910 return GenResult.mcv(.{ .load_symbol = sym_index });
910 }911 }
911 const sym_index = try macho_file.getZigObject().?.getOrCreateMetadataForDecl(macho_file, decl_index);912 const sym_index = try zo.getOrCreateMetadataForDecl(macho_file, decl_index);
912 const sym = macho_file.getSymbol(sym_index);913 const sym = zo.symbols.items[sym_index];
913 if (is_threadlocal) {914 if (is_threadlocal) {
914 return GenResult.mcv(.{ .load_tlv = sym.nlist_idx });915 return GenResult.mcv(.{ .load_tlv = sym.nlist_idx });
915 }916 }
...@@ -956,7 +957,7 @@ fn genUnnamedConst(...@@ -956,7 +957,7 @@ fn genUnnamedConst(
956 },957 },
957 .macho => {958 .macho => {
958 const macho_file = lf.cast(link.File.MachO).?;959 const macho_file = lf.cast(link.File.MachO).?;
959 const local = macho_file.getSymbol(local_sym_index);960 const local = macho_file.getZigObject().?.symbols.items[local_sym_index];
960 return GenResult.mcv(.{ .load_symbol = local.nlist_idx });961 return GenResult.mcv(.{ .load_symbol = local.nlist_idx });
961 },962 },
962 .coff => {963 .coff => {
src/link/MachO.zig+664-1001
...@@ -22,16 +22,11 @@ dylibs: std.ArrayListUnmanaged(File.Index) = .{},...@@ -22,16 +22,11 @@ dylibs: std.ArrayListUnmanaged(File.Index) = .{},
22segments: std.ArrayListUnmanaged(macho.segment_command_64) = .{},22segments: std.ArrayListUnmanaged(macho.segment_command_64) = .{},
23sections: std.MultiArrayList(Section) = .{},23sections: std.MultiArrayList(Section) = .{},
2424
25symbols: std.ArrayListUnmanaged(Symbol) = .{},25resolver: SymbolResolver = .{},
26symbols_extra: std.ArrayListUnmanaged(u32) = .{},
27symbols_free_list: std.ArrayListUnmanaged(Symbol.Index) = .{},
28globals: std.AutoArrayHashMapUnmanaged(u32, Symbol.Index) = .{},
29/// This table will be populated after `scanRelocs` has run.26/// This table will be populated after `scanRelocs` has run.
30/// Key is symbol index.27/// Key is symbol index.
31undefs: std.AutoHashMapUnmanaged(Symbol.Index, std.ArrayListUnmanaged(Atom.Index)) = .{},28undefs: std.AutoHashMapUnmanaged(SymbolResolver.Index, std.ArrayListUnmanaged(Ref)) = .{},
32/// Global symbols we need to resolve for the link to succeed.29dupes: std.AutoHashMapUnmanaged(SymbolResolver.Index, std.ArrayListUnmanaged(File.Index)) = .{},
33undefined_symbols: std.ArrayListUnmanaged(Symbol.Index) = .{},
34boundary_symbols: std.ArrayListUnmanaged(Symbol.Index) = .{},
3530
36dyld_info_cmd: macho.dyld_info_command = .{},31dyld_info_cmd: macho.dyld_info_command = .{},
37symtab_cmd: macho.symtab_command = .{},32symtab_cmd: macho.symtab_command = .{},
...@@ -55,22 +50,7 @@ eh_frame_sect_index: ?u8 = null,...@@ -55,22 +50,7 @@ eh_frame_sect_index: ?u8 = null,
55unwind_info_sect_index: ?u8 = null,50unwind_info_sect_index: ?u8 = null,
56objc_stubs_sect_index: ?u8 = null,51objc_stubs_sect_index: ?u8 = null,
5752
58mh_execute_header_index: ?Symbol.Index = null,
59mh_dylib_header_index: ?Symbol.Index = null,
60dyld_private_index: ?Symbol.Index = null,
61dyld_stub_binder_index: ?Symbol.Index = null,
62dso_handle_index: ?Symbol.Index = null,
63objc_msg_send_index: ?Symbol.Index = null,
64entry_index: ?Symbol.Index = null,
65
66/// List of atoms that are either synthetic or map directly to the Zig source program.
67atoms: std.ArrayListUnmanaged(Atom) = .{},
68atoms_extra: std.ArrayListUnmanaged(u32) = .{},
69thunks: std.ArrayListUnmanaged(Thunk) = .{},53thunks: std.ArrayListUnmanaged(Thunk) = .{},
70unwind_records: std.ArrayListUnmanaged(UnwindInfo.Record) = .{},
71
72/// String interning table
73strings: StringTable = .{},
7454
75/// Output synthetic sections55/// Output synthetic sections
76symtab: std.ArrayListUnmanaged(macho.nlist_64) = .{},56symtab: std.ArrayListUnmanaged(macho.nlist_64) = .{},
...@@ -83,12 +63,13 @@ stubs_helper: StubsHelperSection = .{},...@@ -83,12 +63,13 @@ stubs_helper: StubsHelperSection = .{},
83objc_stubs: ObjcStubsSection = .{},63objc_stubs: ObjcStubsSection = .{},
84la_symbol_ptr: LaSymbolPtrSection = .{},64la_symbol_ptr: LaSymbolPtrSection = .{},
85tlv_ptr: TlvPtrSection = .{},65tlv_ptr: TlvPtrSection = .{},
86rebase: RebaseSection = .{},66rebase: Rebase = .{},
87bind: BindSection = .{},67bind: Bind = .{},
88weak_bind: WeakBindSection = .{},68weak_bind: WeakBind = .{},
89lazy_bind: LazyBindSection = .{},69lazy_bind: LazyBind = .{},
90export_trie: ExportTrieSection = .{},70export_trie: ExportTrie = .{},
91unwind_info: UnwindInfo = .{},71unwind_info: UnwindInfo = .{},
72data_in_code: DataInCode = .{},
9273
93/// Tracked loadable segments during incremental linking.74/// Tracked loadable segments during incremental linking.
94zig_text_seg_index: ?u8 = null,75zig_text_seg_index: ?u8 = null,
...@@ -245,15 +226,8 @@ pub fn createEmpty(...@@ -245,15 +226,8 @@ pub fn createEmpty(
245226
246 // Append null file227 // Append null file
247 try self.files.append(gpa, .null);228 try self.files.append(gpa, .null);
248 // Atom at index 0 is reserved as null atom
249 try self.atoms.append(gpa, .{});
250 try self.atoms_extra.append(gpa, 0);
251 // Append empty string to string tables229 // Append empty string to string tables
252 try self.strings.buffer.append(gpa, 0);
253 try self.strtab.append(gpa, 0);230 try self.strtab.append(gpa, 0);
254 // Append null symbols
255 try self.symbols.append(gpa, .{});
256 try self.symbols_extra.append(gpa, 0);
257231
258 if (opt_zcu) |zcu| {232 if (opt_zcu) |zcu| {
259 if (!use_llvm) {233 if (!use_llvm) {
...@@ -317,15 +291,20 @@ pub fn deinit(self: *MachO) void {...@@ -317,15 +291,20 @@ pub fn deinit(self: *MachO) void {
317 self.dylibs.deinit(gpa);291 self.dylibs.deinit(gpa);
318292
319 self.segments.deinit(gpa);293 self.segments.deinit(gpa);
320 for (self.sections.items(.atoms)) |*list| {294 for (
321 list.deinit(gpa);295 self.sections.items(.atoms),
296 self.sections.items(.out),
297 self.sections.items(.thunks),
298 self.sections.items(.relocs),
299 ) |*atoms, *out, *thnks, *relocs| {
300 atoms.deinit(gpa);
301 out.deinit(gpa);
302 thnks.deinit(gpa);
303 relocs.deinit(gpa);
322 }304 }
323 self.sections.deinit(gpa);305 self.sections.deinit(gpa);
324306
325 self.symbols.deinit(gpa);307 self.resolver.deinit(gpa);
326 self.symbols_extra.deinit(gpa);
327 self.symbols_free_list.deinit(gpa);
328 self.globals.deinit(gpa);
329 {308 {
330 var it = self.undefs.iterator();309 var it = self.undefs.iterator();
331 while (it.next()) |entry| {310 while (it.next()) |entry| {
...@@ -333,10 +312,14 @@ pub fn deinit(self: *MachO) void {...@@ -333,10 +312,14 @@ pub fn deinit(self: *MachO) void {
333 }312 }
334 self.undefs.deinit(gpa);313 self.undefs.deinit(gpa);
335 }314 }
336 self.undefined_symbols.deinit(gpa);315 {
337 self.boundary_symbols.deinit(gpa);316 var it = self.dupes.iterator();
317 while (it.next()) |entry| {
318 entry.value_ptr.deinit(gpa);
319 }
320 self.dupes.deinit(gpa);
321 }
338322
339 self.strings.deinit(gpa);
340 self.symtab.deinit(gpa);323 self.symtab.deinit(gpa);
341 self.strtab.deinit(gpa);324 self.strtab.deinit(gpa);
342 self.got.deinit(gpa);325 self.got.deinit(gpa);
...@@ -350,14 +333,9 @@ pub fn deinit(self: *MachO) void {...@@ -350,14 +333,9 @@ pub fn deinit(self: *MachO) void {
350 self.lazy_bind.deinit(gpa);333 self.lazy_bind.deinit(gpa);
351 self.export_trie.deinit(gpa);334 self.export_trie.deinit(gpa);
352 self.unwind_info.deinit(gpa);335 self.unwind_info.deinit(gpa);
336 self.data_in_code.deinit(gpa);
353337
354 self.atoms.deinit(gpa);
355 self.atoms_extra.deinit(gpa);
356 for (self.thunks.items) |*thunk| {
357 thunk.deinit(gpa);
358 }
359 self.thunks.deinit(gpa);338 self.thunks.deinit(gpa);
360 self.unwind_records.deinit(gpa);
361}339}
362340
363pub fn flush(self: *MachO, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: std.Progress.Node) link.File.FlushError!void {341pub fn flush(self: *MachO, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: std.Progress.Node) link.File.FlushError!void {
...@@ -535,17 +513,14 @@ pub fn flushModule(self: *MachO, arena: Allocator, tid: Zcu.PerThread.Id, prog_n...@@ -535,17 +513,14 @@ pub fn flushModule(self: *MachO, arena: Allocator, tid: Zcu.PerThread.Id, prog_n
535 const index = @as(File.Index, @intCast(try self.files.addOne(gpa)));513 const index = @as(File.Index, @intCast(try self.files.addOne(gpa)));
536 self.files.set(index, .{ .internal = .{ .index = index } });514 self.files.set(index, .{ .internal = .{ .index = index } });
537 self.internal_object = index;515 self.internal_object = index;
516 const object = self.getInternalObject().?;
517 try object.init(gpa);
518 try object.initSymbols(self);
538 }519 }
539520
540 try self.addUndefinedGlobals();
541 try self.resolveSymbols();521 try self.resolveSymbols();
542 try self.parseDebugInfo();522 try self.convertTentativeDefsAndResolveSpecialSymbols();
543 try self.resolveSyntheticSymbols();
544
545 try self.convertTentativeDefinitions();
546 try self.createObjcSections();
547 try self.dedupLiterals();523 try self.dedupLiterals();
548 try self.claimUnresolved();
549524
550 if (self.base.gc_sections) {525 if (self.base.gc_sections) {
551 try dead_strip.gcAtoms(self);526 try dead_strip.gcAtoms(self);
...@@ -567,6 +542,8 @@ pub fn flushModule(self: *MachO, arena: Allocator, tid: Zcu.PerThread.Id, prog_n...@@ -567,6 +542,8 @@ pub fn flushModule(self: *MachO, arena: Allocator, tid: Zcu.PerThread.Id, prog_n
567 dylib.ordinal = @intCast(ord);542 dylib.ordinal = @intCast(ord);
568 }543 }
569544
545 self.claimUnresolved();
546
570 self.scanRelocs() catch |err| switch (err) {547 self.scanRelocs() catch |err| switch (err) {
571 error.HasUndefinedSymbols => return error.FlushFailure,548 error.HasUndefinedSymbols => return error.FlushFailure,
572 else => |e| {549 else => |e| {
...@@ -580,92 +557,38 @@ pub fn flushModule(self: *MachO, arena: Allocator, tid: Zcu.PerThread.Id, prog_n...@@ -580,92 +557,38 @@ pub fn flushModule(self: *MachO, arena: Allocator, tid: Zcu.PerThread.Id, prog_n
580 try self.sortSections();557 try self.sortSections();
581 try self.addAtomsToSections();558 try self.addAtomsToSections();
582 try self.calcSectionSizes();559 try self.calcSectionSizes();
560
583 try self.generateUnwindInfo();561 try self.generateUnwindInfo();
584 try self.initSegments();
585562
563 try self.initSegments();
586 try self.allocateSections();564 try self.allocateSections();
587 self.allocateSegments();565 self.allocateSegments();
588 self.allocateSyntheticSymbols();566 self.allocateSyntheticSymbols();
589 try self.allocateLinkeditSegment();
590567
591 if (build_options.enable_logging) {568 if (build_options.enable_logging) {
592 state_log.debug("{}", .{self.dumpState()});569 state_log.debug("{}", .{self.dumpState()});
593 }570 }
594571
595 try self.initDyldInfoSections();
596
597 // Beyond this point, everything has been allocated a virtual address and we can resolve572 // Beyond this point, everything has been allocated a virtual address and we can resolve
598 // the relocations, and commit objects to file.573 // the relocations, and commit objects to file.
599 if (self.getZigObject()) |zo| {574 try self.resizeSections();
600 var has_resolve_error = false;
601
602 for (zo.atoms.items) |atom_index| {
603 const atom = self.getAtom(atom_index) orelse continue;
604 if (!atom.flags.alive) continue;
605 const sect = &self.sections.items(.header)[atom.out_n_sect];
606 if (sect.isZerofill()) continue;
607 if (!self.isZigSection(atom.out_n_sect)) continue; // Non-Zig sections are handled separately
608 if (atom.getRelocs(self).len == 0) continue;
609 // TODO: we will resolve and write ZigObject's TLS data twice:
610 // once here, and once in writeAtoms
611 const atom_size = math.cast(usize, atom.size) orelse return error.Overflow;
612 const code = try gpa.alloc(u8, atom_size);
613 defer gpa.free(code);
614 atom.getData(self, code) catch |err| switch (err) {
615 error.InputOutput => {
616 try self.reportUnexpectedError("fetching code for '{s}' failed", .{
617 atom.getName(self),
618 });
619 return error.FlushFailure;
620 },
621 else => |e| {
622 try self.reportUnexpectedError("unexpected error while fetching code for '{s}': {s}", .{
623 atom.getName(self),
624 @errorName(e),
625 });
626 return error.FlushFailure;
627 },
628 };
629 const file_offset = sect.offset + atom.value;
630 atom.resolveRelocs(self, code) catch |err| switch (err) {
631 error.ResolveFailed => has_resolve_error = true,
632 else => |e| {
633 try self.reportUnexpectedError("unexpected error while resolving relocations", .{});
634 return e;
635 },
636 };
637 try self.base.file.?.pwriteAll(code, file_offset);
638 }
639575
640 if (has_resolve_error) return error.FlushFailure;576 if (self.getZigObject()) |zo| {
577 zo.resolveRelocs(self) catch |err| switch (err) {
578 error.ResolveFailed => return error.FlushFailure,
579 else => |e| return e,
580 };
641 }581 }
642582 self.writeSectionsAndUpdateLinkeditSizes() catch |err| {
643 self.writeAtoms() catch |err| switch (err) {583 switch (err) {
644 error.ResolveFailed => return error.FlushFailure,584 error.ResolveFailed => return error.FlushFailure,
645 else => |e| {585 else => |e| return e,
646 try self.reportUnexpectedError("unexpected error while resolving relocations", .{});586 }
647 return e;
648 },
649 };587 };
650 try self.writeUnwindInfo();
651 try self.finalizeDyldInfoSections();
652 try self.writeSyntheticSections();
653
654 var off = math.cast(u32, self.getLinkeditSegment().fileoff) orelse return error.Overflow;
655 off = try self.writeDyldInfoSections(off);
656 off = mem.alignForward(u32, off, @alignOf(u64));
657 off = try self.writeFunctionStarts(off);
658 off = mem.alignForward(u32, off, @alignOf(u64));
659 off = try self.writeDataInCode(self.getTextSegment().vmaddr, off);
660 try self.calcSymtabSize();
661 off = mem.alignForward(u32, off, @alignOf(u64));
662 off = try self.writeSymtab(off);
663 off = mem.alignForward(u32, off, @alignOf(u32));
664 off = try self.writeIndsymtab(off);
665 off = mem.alignForward(u32, off, @alignOf(u64));
666 off = try self.writeStrtab(off);
667588
668 self.getLinkeditSegment().filesize = off - self.getLinkeditSegment().fileoff;589 try self.writeSectionsToFile();
590 try self.allocateLinkeditSegment();
591 try self.writeLinkeditSectionsToFile();
669592
670 var codesig: ?CodeSignature = if (self.requiresCodeSig()) blk: {593 var codesig: ?CodeSignature = if (self.requiresCodeSig()) blk: {
671 // Preallocate space for the code signature.594 // Preallocate space for the code signature.
...@@ -982,12 +905,15 @@ fn parseObject(self: *MachO, path: []const u8) ParseError!void {...@@ -982,12 +905,15 @@ fn parseObject(self: *MachO, path: []const u8) ParseError!void {
982 break :mtime @as(u64, @intCast(@divFloor(stat.mtime, 1_000_000_000)));905 break :mtime @as(u64, @intCast(@divFloor(stat.mtime, 1_000_000_000)));
983 };906 };
984 const index = @as(File.Index, @intCast(try self.files.addOne(gpa)));907 const index = @as(File.Index, @intCast(try self.files.addOne(gpa)));
985 self.files.set(index, .{ .object = .{908 self.files.set(index, .{
986 .path = try gpa.dupe(u8, path),909 .object = .{
987 .file_handle = handle,910 .offset = 0, // TODO FAT objects
988 .mtime = mtime,911 .path = try gpa.dupe(u8, path),
989 .index = index,912 .file_handle = handle,
990 } });913 .mtime = mtime,
914 .index = index,
915 },
916 });
991 try self.objects.append(gpa, index);917 try self.objects.append(gpa, index);
992918
993 const object = self.getFile(index).?.object;919 const object = self.getFile(index).?.object;
...@@ -1327,35 +1253,6 @@ fn parseDependentDylibs(self: *MachO) !void {...@@ -1327,35 +1253,6 @@ fn parseDependentDylibs(self: *MachO) !void {
1327 if (has_errors) return error.MissingLibraryDependencies;1253 if (has_errors) return error.MissingLibraryDependencies;
1328}1254}
13291255
1330pub fn addUndefinedGlobals(self: *MachO) !void {
1331 const gpa = self.base.comp.gpa;
1332
1333 try self.undefined_symbols.ensureUnusedCapacity(gpa, self.base.comp.force_undefined_symbols.keys().len);
1334 for (self.base.comp.force_undefined_symbols.keys()) |name| {
1335 const off = try self.strings.insert(gpa, name);
1336 const gop = try self.getOrCreateGlobal(off);
1337 self.undefined_symbols.appendAssumeCapacity(gop.index);
1338 }
1339
1340 if (!self.base.isDynLib() and self.entry_name != null) {
1341 const off = try self.strings.insert(gpa, self.entry_name.?);
1342 const gop = try self.getOrCreateGlobal(off);
1343 self.entry_index = gop.index;
1344 }
1345
1346 {
1347 const off = try self.strings.insert(gpa, "dyld_stub_binder");
1348 const gop = try self.getOrCreateGlobal(off);
1349 self.dyld_stub_binder_index = gop.index;
1350 }
1351
1352 {
1353 const off = try self.strings.insert(gpa, "_objc_msgSend");
1354 const gop = try self.getOrCreateGlobal(off);
1355 self.objc_msg_send_index = gop.index;
1356 }
1357}
1358
1359/// When resolving symbols, we approach the problem similarly to `mold`.1256/// When resolving symbols, we approach the problem similarly to `mold`.
1360/// 1. Resolve symbols across all objects (including those preemptively extracted archives).1257/// 1. Resolve symbols across all objects (including those preemptively extracted archives).
1361/// 2. Resolve symbols across all shared objects.1258/// 2. Resolve symbols across all shared objects.
...@@ -1368,18 +1265,17 @@ pub fn resolveSymbols(self: *MachO) !void {...@@ -1368,18 +1265,17 @@ pub fn resolveSymbols(self: *MachO) !void {
1368 defer tracy.end();1265 defer tracy.end();
13691266
1370 // Resolve symbols in the ZigObject. For now, we assume that it's always live.1267 // Resolve symbols in the ZigObject. For now, we assume that it's always live.
1371 if (self.getZigObject()) |zo| zo.asFile().resolveSymbols(self);1268 if (self.getZigObject()) |zo| try zo.asFile().resolveSymbols(self);
1372 // Resolve symbols on the set of all objects and shared objects (even if some are unneeded).1269 // Resolve symbols on the set of all objects and shared objects (even if some are unneeded).
1373 for (self.objects.items) |index| self.getFile(index).?.resolveSymbols(self);1270 for (self.objects.items) |index| try self.getFile(index).?.resolveSymbols(self);
1374 for (self.dylibs.items) |index| self.getFile(index).?.resolveSymbols(self);1271 for (self.dylibs.items) |index| try self.getFile(index).?.resolveSymbols(self);
1272 if (self.getInternalObject()) |obj| try obj.resolveSymbols(self);
13751273
1376 // Mark live objects.1274 // Mark live objects.
1377 self.markLive();1275 self.markLive();
13781276
1379 // Reset state of all globals after marking live objects.1277 // Reset state of all globals after marking live objects.
1380 if (self.getZigObject()) |zo| zo.asFile().resetGlobals(self);1278 self.resolver.reset();
1381 for (self.objects.items) |index| self.getFile(index).?.resetGlobals(self);
1382 for (self.dylibs.items) |index| self.getFile(index).?.resetGlobals(self);
13831279
1384 // Prune dead objects.1280 // Prune dead objects.
1385 var i: usize = 0;1281 var i: usize = 0;
...@@ -1393,37 +1289,26 @@ pub fn resolveSymbols(self: *MachO) !void {...@@ -1393,37 +1289,26 @@ pub fn resolveSymbols(self: *MachO) !void {
1393 }1289 }
13941290
1395 // Re-resolve the symbols.1291 // Re-resolve the symbols.
1396 if (self.getZigObject()) |zo| zo.resolveSymbols(self);1292 if (self.getZigObject()) |zo| try zo.resolveSymbols(self);
1397 for (self.objects.items) |index| self.getFile(index).?.resolveSymbols(self);1293 for (self.objects.items) |index| try self.getFile(index).?.resolveSymbols(self);
1398 for (self.dylibs.items) |index| self.getFile(index).?.resolveSymbols(self);1294 for (self.dylibs.items) |index| try self.getFile(index).?.resolveSymbols(self);
1295 if (self.getInternalObject()) |obj| try obj.resolveSymbols(self);
1296
1297 // Merge symbol visibility
1298 if (self.getZigObject()) |zo| zo.mergeSymbolVisibility(self);
1299 for (self.objects.items) |index| self.getFile(index).?.object.mergeSymbolVisibility(self);
1399}1300}
14001301
1401fn markLive(self: *MachO) void {1302fn markLive(self: *MachO) void {
1402 const tracy = trace(@src());1303 const tracy = trace(@src());
1403 defer tracy.end();1304 defer tracy.end();
14041305
1405 for (self.undefined_symbols.items) |index| {
1406 if (self.getSymbol(index).getFile(self)) |file| {
1407 if (file == .object) file.object.alive = true;
1408 }
1409 }
1410 if (self.entry_index) |index| {
1411 const sym = self.getSymbol(index);
1412 if (sym.getFile(self)) |file| {
1413 if (file == .object) file.object.alive = true;
1414 }
1415 }
1416 if (self.getZigObject()) |zo| zo.markLive(self);1306 if (self.getZigObject()) |zo| zo.markLive(self);
1417 for (self.objects.items) |index| {1307 for (self.objects.items) |index| {
1418 const object = self.getFile(index).?.object;1308 const object = self.getFile(index).?.object;
1419 if (object.alive) object.markLive(self);1309 if (object.alive) object.markLive(self);
1420 }1310 }
1421}1311 if (self.getInternalObject()) |obj| obj.markLive(self);
1422
1423pub fn parseDebugInfo(self: *MachO) !void {
1424 for (self.objects.items) |index| {
1425 try self.getFile(index).?.object.parseDebugInfo(self);
1426 }
1427}1312}
14281313
1429fn resolveSyntheticSymbols(self: *MachO) !void {1314fn resolveSyntheticSymbols(self: *MachO) !void {
...@@ -1473,10 +1358,14 @@ fn resolveSyntheticSymbols(self: *MachO) !void {...@@ -1473,10 +1358,14 @@ fn resolveSyntheticSymbols(self: *MachO) !void {
1473 }1358 }
1474}1359}
14751360
1476fn convertTentativeDefinitions(self: *MachO) !void {1361fn convertTentativeDefsAndResolveSpecialSymbols(self: *MachO) !void {
1477 for (self.objects.items) |index| {1362 for (self.objects.items) |index| {
1478 try self.getFile(index).?.object.convertTentativeDefinitions(self);1363 try self.getFile(index).?.object.convertTentativeDefinitions(self);
1479 }1364 }
1365 if (self.getInternalObject()) |obj| {
1366 try obj.resolveBoundarySymbols(self);
1367 try obj.resolveObjcMsgSendSymbols(self);
1368 }
1480}1369}
14811370
1482fn createObjcSections(self: *MachO) !void {1371fn createObjcSections(self: *MachO) !void {
...@@ -1514,6 +1403,9 @@ fn createObjcSections(self: *MachO) !void {...@@ -1514,6 +1403,9 @@ fn createObjcSections(self: *MachO) !void {
1514}1403}
15151404
1516pub fn dedupLiterals(self: *MachO) !void {1405pub fn dedupLiterals(self: *MachO) !void {
1406 const tracy = trace(@src());
1407 defer tracy.end();
1408
1517 const gpa = self.base.comp.gpa;1409 const gpa = self.base.comp.gpa;
1518 var lp: LiteralPool = .{};1410 var lp: LiteralPool = .{};
1519 defer lp.deinit(gpa);1411 defer lp.deinit(gpa);
...@@ -1539,80 +1431,46 @@ pub fn dedupLiterals(self: *MachO) !void {...@@ -1539,80 +1431,46 @@ pub fn dedupLiterals(self: *MachO) !void {
1539 }1431 }
1540}1432}
15411433
1542fn claimUnresolved(self: *MachO) error{OutOfMemory}!void {1434fn claimUnresolved(self: *MachO) void {
1543 if (self.getZigObject()) |zo| {1435 if (self.getZigObject()) |zo| {
1544 try zo.asFile().claimUnresolved(self);1436 zo.asFile().claimUnresolved(self);
1545 }1437 }
1546 for (self.objects.items) |index| {1438 for (self.objects.items) |index| {
1547 try self.getFile(index).?.claimUnresolved(self);1439 self.getFile(index).?.claimUnresolved(self);
1548 }1440 }
1549}1441}
15501442
1551fn checkDuplicates(self: *MachO) !void {1443fn checkDuplicates(self: *MachO) !void {
1552 const gpa = self.base.comp.gpa;
1553
1554 var dupes = std.AutoArrayHashMap(Symbol.Index, std.ArrayListUnmanaged(File.Index)).init(gpa);
1555 defer {
1556 for (dupes.values()) |*list| {
1557 list.deinit(gpa);
1558 }
1559 dupes.deinit();
1560 }
1561
1562 if (self.getZigObject()) |zo| {1444 if (self.getZigObject()) |zo| {
1563 try zo.checkDuplicates(&dupes, self);1445 try zo.asFile().checkDuplicates(self);
1564 }1446 }
1565
1566 for (self.objects.items) |index| {1447 for (self.objects.items) |index| {
1567 try self.getFile(index).?.object.checkDuplicates(&dupes, self);1448 try self.getFile(index).?.checkDuplicates(self);
1568 }1449 }
15691450 if (self.getInternalObject()) |obj| {
1570 try self.reportDuplicates(dupes);1451 try obj.asFile().checkDuplicates(self);
1452 }
1453 try self.reportDuplicates();
1571}1454}
15721455
1573fn markImportsAndExports(self: *MachO) void {1456fn markImportsAndExports(self: *MachO) void {
1457 const tracy = trace(@src());
1458 defer tracy.end();
1459
1574 if (self.getZigObject()) |zo| {1460 if (self.getZigObject()) |zo| {
1575 zo.asFile().markImportsExports(self);1461 zo.asFile().markImportsExports(self);
1576 }1462 }
1577 for (self.objects.items) |index| {1463 for (self.objects.items) |index| {
1578 self.getFile(index).?.markImportsExports(self);1464 self.getFile(index).?.markImportsExports(self);
1579 }1465 }
15801466 if (self.getInternalObject()) |obj| {
1581 for (self.undefined_symbols.items) |index| {1467 obj.asFile().markImportsExports(self);
1582 const sym = self.getSymbol(index);
1583 if (sym.getFile(self)) |file| {
1584 if (sym.visibility != .global) continue;
1585 if (file == .dylib and !sym.flags.abs) sym.flags.import = true;
1586 }
1587 }
1588
1589 for (&[_]?Symbol.Index{
1590 self.entry_index,
1591 self.dyld_stub_binder_index,
1592 self.objc_msg_send_index,
1593 }) |index| {
1594 if (index) |idx| {
1595 const sym = self.getSymbol(idx);
1596 if (sym.getFile(self)) |file| {
1597 if (file == .dylib) sym.flags.import = true;
1598 }
1599 }
1600 }1468 }
1601}1469}
16021470
1603fn deadStripDylibs(self: *MachO) void {1471fn deadStripDylibs(self: *MachO) void {
1604 for (&[_]?Symbol.Index{1472 const tracy = trace(@src());
1605 self.entry_index,1473 defer tracy.end();
1606 self.dyld_stub_binder_index,
1607 self.objc_msg_send_index,
1608 }) |index| {
1609 if (index) |idx| {
1610 const sym = self.getSymbol(idx);
1611 if (sym.getFile(self)) |file| {
1612 if (file == .dylib) file.dylib.referenced = true;
1613 }
1614 }
1615 }
16161474
1617 for (self.dylibs.items) |index| {1475 for (self.dylibs.items) |index| {
1618 self.getFile(index).?.dylib.markReferenced(self);1476 self.getFile(index).?.dylib.markReferenced(self);
...@@ -1633,50 +1491,29 @@ fn scanRelocs(self: *MachO) !void {...@@ -1633,50 +1491,29 @@ fn scanRelocs(self: *MachO) !void {
1633 const tracy = trace(@src());1491 const tracy = trace(@src());
1634 defer tracy.end();1492 defer tracy.end();
16351493
1636 if (self.getZigObject()) |zo| try zo.scanRelocs(self);1494 if (self.getZigObject()) |zo| {
16371495 try zo.scanRelocs(self);
1496 }
1638 for (self.objects.items) |index| {1497 for (self.objects.items) |index| {
1639 try self.getFile(index).?.object.scanRelocs(self);1498 try self.getFile(index).?.object.scanRelocs(self);
1640 }1499 }
1500 if (self.getInternalObject()) |obj| {
1501 obj.scanRelocs(self);
1502 }
16411503
1642 try self.reportUndefs();1504 try self.reportUndefs();
16431505
1644 if (self.entry_index) |index| {1506 if (self.getZigObject()) |zo| {
1645 const sym = self.getSymbol(index);1507 try zo.asFile().createSymbolIndirection(self);
1646 if (sym.getFile(self) != null) {
1647 if (sym.flags.import) sym.flags.stubs = true;
1648 }
1649 }1508 }
16501509 for (self.objects.items) |index| {
1651 if (self.dyld_stub_binder_index) |index| {1510 try self.getFile(index).?.createSymbolIndirection(self);
1652 const sym = self.getSymbol(index);
1653 if (sym.getFile(self) != null) sym.flags.needs_got = true;
1654 }1511 }
16551512 for (self.dylibs.items) |index| {
1656 if (self.objc_msg_send_index) |index| {1513 try self.getFile(index).?.createSymbolIndirection(self);
1657 const sym = self.getSymbol(index);
1658 if (sym.getFile(self) != null)
1659 sym.flags.needs_got = true; // TODO is it always needed, or only if we are synthesising fast stubs?
1660 }1514 }
16611515 if (self.getInternalObject()) |obj| {
1662 for (self.symbols.items, 0..) |*symbol, i| {1516 try obj.asFile().createSymbolIndirection(self);
1663 const index = @as(Symbol.Index, @intCast(i));
1664 if (symbol.flags.needs_got) {
1665 log.debug("'{s}' needs GOT", .{symbol.getName(self)});
1666 try self.got.addSymbol(index, self);
1667 }
1668 if (symbol.flags.stubs) {
1669 log.debug("'{s}' needs STUBS", .{symbol.getName(self)});
1670 try self.stubs.addSymbol(index, self);
1671 }
1672 if (symbol.flags.tlv_ptr) {
1673 log.debug("'{s}' needs TLV pointer", .{symbol.getName(self)});
1674 try self.tlv_ptr.addSymbol(index, self);
1675 }
1676 if (symbol.flags.objc_stubs) {
1677 log.debug("'{s}' needs OBJC STUBS", .{symbol.getName(self)});
1678 try self.objc_stubs.addSymbol(index, self);
1679 }
1680 }1517 }
1681}1518}
16821519
...@@ -1684,17 +1521,15 @@ fn reportUndefs(self: *MachO) !void {...@@ -1684,17 +1521,15 @@ fn reportUndefs(self: *MachO) !void {
1684 const tracy = trace(@src());1521 const tracy = trace(@src());
1685 defer tracy.end();1522 defer tracy.end();
16861523
1687 switch (self.undefined_treatment) {1524 if (self.undefined_treatment == .suppress or
1688 .dynamic_lookup, .suppress => return,1525 self.undefined_treatment == .dynamic_lookup) return;
1689 .@"error", .warn => {},
1690 }
16911526
1692 const max_notes = 4;1527 const max_notes = 4;
16931528
1694 var has_undefs = false;1529 var has_undefs = false;
1695 var it = self.undefs.iterator();1530 var it = self.undefs.iterator();
1696 while (it.next()) |entry| {1531 while (it.next()) |entry| {
1697 const undef_sym = self.getSymbol(entry.key_ptr.*);1532 const undef_sym = self.resolver.keys.items[entry.key_ptr.* - 1];
1698 const notes = entry.value_ptr.*;1533 const notes = entry.value_ptr.*;
1699 const nnotes = @min(notes.items.len, max_notes) + @intFromBool(notes.items.len > max_notes);1534 const nnotes = @min(notes.items.len, max_notes) + @intFromBool(notes.items.len > max_notes);
17001535
...@@ -1704,8 +1539,9 @@ fn reportUndefs(self: *MachO) !void {...@@ -1704,8 +1539,9 @@ fn reportUndefs(self: *MachO) !void {
17041539
1705 var inote: usize = 0;1540 var inote: usize = 0;
1706 while (inote < @min(notes.items.len, max_notes)) : (inote += 1) {1541 while (inote < @min(notes.items.len, max_notes)) : (inote += 1) {
1707 const atom = self.getAtom(notes.items[inote]).?;1542 const note = notes.items[inote];
1708 const file = atom.getFile(self);1543 const file = self.getFile(note.file).?;
1544 const atom = note.getAtom(self).?;
1709 try err.addNote(self, "referenced by {}:{s}", .{ file.fmtPath(), atom.getName(self) });1545 try err.addNote(self, "referenced by {}:{s}", .{ file.fmtPath(), atom.getName(self) });
1710 }1546 }
17111547
...@@ -1714,64 +1550,18 @@ fn reportUndefs(self: *MachO) !void {...@@ -1714,64 +1550,18 @@ fn reportUndefs(self: *MachO) !void {
1714 try err.addNote(self, "referenced {d} more times", .{remaining});1550 try err.addNote(self, "referenced {d} more times", .{remaining});
1715 }1551 }
1716 }1552 }
1717
1718 for (self.undefined_symbols.items) |index| {
1719 const sym = self.getSymbol(index);
1720 if (sym.getFile(self) != null) continue; // If undefined in an object file, will be reported above
1721 has_undefs = true;
1722 var err = try self.addErrorWithNotes(1);
1723 try err.addMsg(self, "undefined symbol: {s}", .{sym.getName(self)});
1724 try err.addNote(self, "-u command line option", .{});
1725 }
1726
1727 if (self.entry_index) |index| {
1728 const sym = self.getSymbol(index);
1729 if (sym.getFile(self) == null) {
1730 has_undefs = true;
1731 var err = try self.addErrorWithNotes(1);
1732 try err.addMsg(self, "undefined symbol: {s}", .{sym.getName(self)});
1733 try err.addNote(self, "implicit entry/start for main executable", .{});
1734 }
1735 }
1736
1737 if (self.dyld_stub_binder_index) |index| {
1738 const sym = self.getSymbol(index);
1739 if (sym.getFile(self) == null and self.stubs_sect_index != null) {
1740 has_undefs = true;
1741 var err = try self.addErrorWithNotes(1);
1742 try err.addMsg(self, "undefined symbol: {s}", .{sym.getName(self)});
1743 try err.addNote(self, "implicit -u command line option", .{});
1744 }
1745 }
1746
1747 if (self.objc_msg_send_index) |index| {
1748 const sym = self.getSymbol(index);
1749 if (sym.getFile(self) == null and self.objc_stubs_sect_index != null) {
1750 has_undefs = true;
1751 var err = try self.addErrorWithNotes(1);
1752 try err.addMsg(self, "undefined symbol: {s}", .{sym.getName(self)});
1753 try err.addNote(self, "implicit -u command line option", .{});
1754 }
1755 }
1756
1757 if (has_undefs) return error.HasUndefinedSymbols;1553 if (has_undefs) return error.HasUndefinedSymbols;
1758}1554}
17591555
1760fn initOutputSections(self: *MachO) !void {1556fn initOutputSections(self: *MachO) !void {
1557 const tracy = trace(@src());
1558 defer tracy.end();
1559
1761 for (self.objects.items) |index| {1560 for (self.objects.items) |index| {
1762 const object = self.getFile(index).?.object;1561 try self.getFile(index).?.initOutputSections(self);
1763 for (object.atoms.items) |atom_index| {
1764 const atom = self.getAtom(atom_index) orelse continue;
1765 if (!atom.flags.alive) continue;
1766 atom.out_n_sect = try Atom.initOutputSection(atom.getInputSection(self), self);
1767 }
1768 }1562 }
1769 if (self.getInternalObject()) |object| {1563 if (self.getInternalObject()) |obj| {
1770 for (object.atoms.items) |atom_index| {1564 try obj.asFile().initOutputSections(self);
1771 const atom = self.getAtom(atom_index) orelse continue;
1772 if (!atom.flags.alive) continue;
1773 atom.out_n_sect = try Atom.initOutputSection(atom.getInputSection(self), self);
1774 }
1775 }1565 }
1776 self.text_sect_index = self.getSectionByName("__TEXT", "__text") orelse1566 self.text_sect_index = self.getSectionByName("__TEXT", "__text") orelse
1777 try self.addSection("__TEXT", "__text", .{1567 try self.addSection("__TEXT", "__text", .{
...@@ -1844,46 +1634,50 @@ fn initSyntheticSections(self: *MachO) !void {...@@ -1844,46 +1634,50 @@ fn initSyntheticSections(self: *MachO) !void {
1844 self.eh_frame_sect_index = try self.addSection("__TEXT", "__eh_frame", .{});1634 self.eh_frame_sect_index = try self.addSection("__TEXT", "__eh_frame", .{});
1845 }1635 }
18461636
1847 for (self.boundary_symbols.items) |sym_index| {1637 if (self.getInternalObject()) |obj| {
1848 const gpa = self.base.comp.gpa;1638 const gpa = self.base.comp.gpa;
1849 const sym = self.getSymbol(sym_index);1639
1850 const name = sym.getName(self);1640 for (obj.boundary_symbols.items) |sym_index| {
18511641 const ref = obj.getSymbolRef(sym_index, self);
1852 if (eatPrefix(name, "segment$start$")) |segname| {1642 const sym = ref.getSymbol(self).?;
1853 if (self.getSegmentByName(segname) == null) { // TODO check segname is valid1643 const name = sym.getName(self);
1854 const prot = getSegmentProt(segname);1644
1855 _ = try self.segments.append(gpa, .{1645 if (eatPrefix(name, "segment$start$")) |segname| {
1856 .cmdsize = @sizeOf(macho.segment_command_64),1646 if (self.getSegmentByName(segname) == null) { // TODO check segname is valid
1857 .segname = makeStaticString(segname),1647 const prot = getSegmentProt(segname);
1858 .initprot = prot,1648 _ = try self.segments.append(gpa, .{
1859 .maxprot = prot,1649 .cmdsize = @sizeOf(macho.segment_command_64),
1860 });1650 .segname = makeStaticString(segname),
1861 }1651 .initprot = prot,
1862 } else if (eatPrefix(name, "segment$stop$")) |segname| {1652 .maxprot = prot,
1863 if (self.getSegmentByName(segname) == null) { // TODO check segname is valid1653 });
1864 const prot = getSegmentProt(segname);1654 }
1865 _ = try self.segments.append(gpa, .{1655 } else if (eatPrefix(name, "segment$stop$")) |segname| {
1866 .cmdsize = @sizeOf(macho.segment_command_64),1656 if (self.getSegmentByName(segname) == null) { // TODO check segname is valid
1867 .segname = makeStaticString(segname),1657 const prot = getSegmentProt(segname);
1868 .initprot = prot,1658 _ = try self.segments.append(gpa, .{
1869 .maxprot = prot,1659 .cmdsize = @sizeOf(macho.segment_command_64),
1870 });1660 .segname = makeStaticString(segname),
1871 }1661 .initprot = prot,
1872 } else if (eatPrefix(name, "section$start$")) |actual_name| {1662 .maxprot = prot,
1873 const sep = mem.indexOfScalar(u8, actual_name, '$').?; // TODO error rather than a panic1663 });
1874 const segname = actual_name[0..sep]; // TODO check segname is valid1664 }
1875 const sectname = actual_name[sep + 1 ..]; // TODO check sectname is valid1665 } else if (eatPrefix(name, "section$start$")) |actual_name| {
1876 if (self.getSectionByName(segname, sectname) == null) {1666 const sep = mem.indexOfScalar(u8, actual_name, '$').?; // TODO error rather than a panic
1877 _ = try self.addSection(segname, sectname, .{});1667 const segname = actual_name[0..sep]; // TODO check segname is valid
1878 }1668 const sectname = actual_name[sep + 1 ..]; // TODO check sectname is valid
1879 } else if (eatPrefix(name, "section$stop$")) |actual_name| {1669 if (self.getSectionByName(segname, sectname) == null) {
1880 const sep = mem.indexOfScalar(u8, actual_name, '$').?; // TODO error rather than a panic1670 _ = try self.addSection(segname, sectname, .{});
1881 const segname = actual_name[0..sep]; // TODO check segname is valid1671 }
1882 const sectname = actual_name[sep + 1 ..]; // TODO check sectname is valid1672 } else if (eatPrefix(name, "section$stop$")) |actual_name| {
1883 if (self.getSectionByName(segname, sectname) == null) {1673 const sep = mem.indexOfScalar(u8, actual_name, '$').?; // TODO error rather than a panic
1884 _ = try self.addSection(segname, sectname, .{});1674 const segname = actual_name[0..sep]; // TODO check segname is valid
1885 }1675 const sectname = actual_name[sep + 1 ..]; // TODO check sectname is valid
1886 } else unreachable;1676 if (self.getSectionByName(segname, sectname) == null) {
1677 _ = try self.addSection(segname, sectname, .{});
1678 }
1679 } else unreachable;
1680 }
1887 }1681 }
1888}1682}
18891683
...@@ -1990,38 +1784,25 @@ pub fn sortSections(self: *MachO) !void {...@@ -1990,38 +1784,25 @@ pub fn sortSections(self: *MachO) !void {
1990 }1784 }
19911785
1992 if (self.getZigObject()) |zo| {1786 if (self.getZigObject()) |zo| {
1993 for (zo.atoms.items) |atom_index| {1787 for (zo.getAtoms()) |atom_index| {
1994 const atom = self.getAtom(atom_index) orelse continue;1788 const atom = zo.getAtom(atom_index) orelse continue;
1995 if (!atom.flags.alive) continue;1789 if (!atom.flags.alive) continue;
1996 atom.out_n_sect = backlinks[atom.out_n_sect];1790 atom.out_n_sect = backlinks[atom.out_n_sect];
1997 }1791 }
1998
1999 for (zo.symtab.items(.nlist)) |*sym| {
2000 if (sym.sect()) {
2001 sym.n_sect = backlinks[sym.n_sect - 1] + 1;
2002 }
2003 }
2004
2005 for (zo.symbols.items) |sym_index| {
2006 const sym = self.getSymbol(sym_index);
2007 const atom = sym.getAtom(self) orelse continue;
2008 if (!atom.flags.alive) continue;
2009 if (sym.getFile(self).?.getIndex() != zo.index) continue;
2010 sym.out_n_sect = backlinks[sym.out_n_sect];
2011 }
2012 }1792 }
20131793
2014 for (self.objects.items) |index| {1794 for (self.objects.items) |index| {
2015 for (self.getFile(index).?.object.atoms.items) |atom_index| {1795 const file = self.getFile(index).?;
2016 const atom = self.getAtom(atom_index) orelse continue;1796 for (file.getAtoms()) |atom_index| {
1797 const atom = file.getAtom(atom_index) orelse continue;
2017 if (!atom.flags.alive) continue;1798 if (!atom.flags.alive) continue;
2018 atom.out_n_sect = backlinks[atom.out_n_sect];1799 atom.out_n_sect = backlinks[atom.out_n_sect];
2019 }1800 }
2020 }1801 }
20211802
2022 if (self.getInternalObject()) |object| {1803 if (self.getInternalObject()) |object| {
2023 for (object.atoms.items) |atom_index| {1804 for (object.getAtoms()) |atom_index| {
2024 const atom = self.getAtom(atom_index) orelse continue;1805 const atom = object.getAtom(atom_index) orelse continue;
2025 if (!atom.flags.alive) continue;1806 if (!atom.flags.alive) continue;
2026 atom.out_n_sect = backlinks[atom.out_n_sect];1807 atom.out_n_sect = backlinks[atom.out_n_sect];
2027 }1808 }
...@@ -2058,35 +1839,32 @@ pub fn addAtomsToSections(self: *MachO) !void {...@@ -2058,35 +1839,32 @@ pub fn addAtomsToSections(self: *MachO) !void {
2058 const tracy = trace(@src());1839 const tracy = trace(@src());
2059 defer tracy.end();1840 defer tracy.end();
20601841
2061 for (self.objects.items) |index| {1842 const gpa = self.base.comp.gpa;
2062 const object = self.getFile(index).?.object;1843
2063 for (object.atoms.items) |atom_index| {1844 if (self.getZigObject()) |zo| {
2064 const atom = self.getAtom(atom_index) orelse continue;1845 for (zo.getAtoms()) |atom_index| {
1846 const atom = zo.getAtom(atom_index) orelse continue;
2065 if (!atom.flags.alive) continue;1847 if (!atom.flags.alive) continue;
1848 if (self.isZigSection(atom.out_n_sect)) continue;
2066 const atoms = &self.sections.items(.atoms)[atom.out_n_sect];1849 const atoms = &self.sections.items(.atoms)[atom.out_n_sect];
2067 try atoms.append(self.base.comp.gpa, atom_index);1850 try atoms.append(gpa, .{ .index = atom_index, .file = zo.index });
2068 }1851 }
2069 for (object.symbols.items) |sym_index| {1852 }
2070 const sym = self.getSymbol(sym_index);1853 for (self.objects.items) |index| {
2071 const atom = sym.getAtom(self) orelse continue;1854 const file = self.getFile(index).?;
1855 for (file.getAtoms()) |atom_index| {
1856 const atom = file.getAtom(atom_index) orelse continue;
2072 if (!atom.flags.alive) continue;1857 if (!atom.flags.alive) continue;
2073 if (sym.getFile(self).?.getIndex() != index) continue;1858 const atoms = &self.sections.items(.atoms)[atom.out_n_sect];
2074 sym.out_n_sect = atom.out_n_sect;1859 try atoms.append(gpa, .{ .index = atom_index, .file = index });
2075 }1860 }
2076 }1861 }
2077 if (self.getInternalObject()) |object| {1862 if (self.getInternalObject()) |object| {
2078 for (object.atoms.items) |atom_index| {1863 for (object.getAtoms()) |atom_index| {
2079 const atom = self.getAtom(atom_index) orelse continue;1864 const atom = object.getAtom(atom_index) orelse continue;
2080 if (!atom.flags.alive) continue;1865 if (!atom.flags.alive) continue;
2081 const atoms = &self.sections.items(.atoms)[atom.out_n_sect];1866 const atoms = &self.sections.items(.atoms)[atom.out_n_sect];
2082 try atoms.append(self.base.comp.gpa, atom_index);1867 try atoms.append(gpa, .{ .index = atom_index, .file = object.index });
2083 }
2084 for (object.symbols.items) |sym_index| {
2085 const sym = self.getSymbol(sym_index);
2086 const atom = sym.getAtom(self) orelse continue;
2087 if (!atom.flags.alive) continue;
2088 if (sym.getFile(self).?.getIndex() != object.index) continue;
2089 sym.out_n_sect = atom.out_n_sect;
2090 }1868 }
2091 }1869 }
2092}1870}
...@@ -2108,8 +1886,8 @@ fn calcSectionSizes(self: *MachO) !void {...@@ -2108,8 +1886,8 @@ fn calcSectionSizes(self: *MachO) !void {
2108 if (atoms.items.len == 0) continue;1886 if (atoms.items.len == 0) continue;
2109 if (self.requiresThunks() and header.isCode()) continue;1887 if (self.requiresThunks() and header.isCode()) continue;
21101888
2111 for (atoms.items) |atom_index| {1889 for (atoms.items) |ref| {
2112 const atom = self.getAtom(atom_index).?;1890 const atom = ref.getAtom(self).?;
2113 const atom_alignment = atom.alignment.toByteUnits() orelse 1;1891 const atom_alignment = atom.alignment.toByteUnits() orelse 1;
2114 const offset = mem.alignForward(u64, header.size, atom_alignment);1892 const offset = mem.alignForward(u64, header.size, atom_alignment);
2115 const padding = offset - header.size;1893 const padding = offset - header.size;
...@@ -2129,6 +1907,22 @@ fn calcSectionSizes(self: *MachO) !void {...@@ -2129,6 +1907,22 @@ fn calcSectionSizes(self: *MachO) !void {
2129 }1907 }
2130 }1908 }
21311909
1910 // At this point, we can also calculate symtab and data-in-code linkedit section sizes
1911 if (self.getZigObject()) |zo| {
1912 zo.asFile().calcSymtabSize(self);
1913 }
1914 for (self.objects.items) |index| {
1915 self.getFile(index).?.calcSymtabSize(self);
1916 }
1917 for (self.dylibs.items) |index| {
1918 self.getFile(index).?.calcSymtabSize(self);
1919 }
1920 if (self.getInternalObject()) |obj| {
1921 obj.asFile().calcSymtabSize(self);
1922 }
1923
1924 try self.calcSymtabSize();
1925
2132 if (self.got_sect_index) |idx| {1926 if (self.got_sect_index) |idx| {
2133 const header = &self.sections.items(.header)[idx];1927 const header = &self.sections.items(.header)[idx];
2134 header.size = self.got.size();1928 header.size = self.got.size();
...@@ -2409,77 +2203,61 @@ fn allocateSegments(self: *MachO) void {...@@ -2409,77 +2203,61 @@ fn allocateSegments(self: *MachO) void {
2409}2203}
24102204
2411fn allocateSyntheticSymbols(self: *MachO) void {2205fn allocateSyntheticSymbols(self: *MachO) void {
2412 const text_seg = self.getTextSegment();2206 if (self.getInternalObject()) |obj| {
24132207 obj.allocateSyntheticSymbols(self);
2414 if (self.mh_execute_header_index) |index| {
2415 const global = self.getSymbol(index);
2416 global.value = text_seg.vmaddr;
2417 }
24182208
2419 if (self.data_sect_index) |idx| {2209 const text_seg = self.getTextSegment();
2420 const sect = self.sections.items(.header)[idx];
2421 for (&[_]?Symbol.Index{
2422 self.dso_handle_index,
2423 self.mh_dylib_header_index,
2424 self.dyld_private_index,
2425 }) |maybe_index| {
2426 if (maybe_index) |index| {
2427 const global = self.getSymbol(index);
2428 global.value = sect.addr;
2429 global.out_n_sect = idx;
2430 }
2431 }
2432 }
24332210
2434 for (self.boundary_symbols.items) |sym_index| {2211 for (obj.boundary_symbols.items) |sym_index| {
2435 const sym = self.getSymbol(sym_index);2212 const ref = obj.getSymbolRef(sym_index, self);
2436 const name = sym.getName(self);2213 const sym = ref.getSymbol(self).?;
2214 const name = sym.getName(self);
24372215
2438 sym.flags.@"export" = false;2216 sym.value = text_seg.vmaddr;
2439 sym.value = text_seg.vmaddr;
24402217
2441 if (mem.startsWith(u8, name, "segment$start$")) {2218 if (mem.startsWith(u8, name, "segment$start$")) {
2442 const segname = name["segment$start$".len..];2219 const segname = name["segment$start$".len..];
2443 if (self.getSegmentByName(segname)) |seg_id| {2220 if (self.getSegmentByName(segname)) |seg_id| {
2444 const seg = self.segments.items[seg_id];2221 const seg = self.segments.items[seg_id];
2445 sym.value = seg.vmaddr;2222 sym.value = seg.vmaddr;
2446 }2223 }
2447 } else if (mem.startsWith(u8, name, "segment$stop$")) {2224 } else if (mem.startsWith(u8, name, "segment$stop$")) {
2448 const segname = name["segment$stop$".len..];2225 const segname = name["segment$stop$".len..];
2449 if (self.getSegmentByName(segname)) |seg_id| {2226 if (self.getSegmentByName(segname)) |seg_id| {
2450 const seg = self.segments.items[seg_id];2227 const seg = self.segments.items[seg_id];
2451 sym.value = seg.vmaddr + seg.vmsize;2228 sym.value = seg.vmaddr + seg.vmsize;
2452 }2229 }
2453 } else if (mem.startsWith(u8, name, "section$start$")) {2230 } else if (mem.startsWith(u8, name, "section$start$")) {
2454 const actual_name = name["section$start$".len..];2231 const actual_name = name["section$start$".len..];
2455 const sep = mem.indexOfScalar(u8, actual_name, '$').?; // TODO error rather than a panic2232 const sep = mem.indexOfScalar(u8, actual_name, '$').?; // TODO error rather than a panic
2456 const segname = actual_name[0..sep];2233 const segname = actual_name[0..sep];
2457 const sectname = actual_name[sep + 1 ..];2234 const sectname = actual_name[sep + 1 ..];
2458 if (self.getSectionByName(segname, sectname)) |sect_id| {2235 if (self.getSectionByName(segname, sectname)) |sect_id| {
2459 const sect = self.sections.items(.header)[sect_id];2236 const sect = self.sections.items(.header)[sect_id];
2460 sym.value = sect.addr;2237 sym.value = sect.addr;
2461 sym.out_n_sect = sect_id;2238 sym.out_n_sect = sect_id;
2462 }2239 }
2463 } else if (mem.startsWith(u8, name, "section$stop$")) {2240 } else if (mem.startsWith(u8, name, "section$stop$")) {
2464 const actual_name = name["section$stop$".len..];2241 const actual_name = name["section$stop$".len..];
2465 const sep = mem.indexOfScalar(u8, actual_name, '$').?; // TODO error rather than a panic2242 const sep = mem.indexOfScalar(u8, actual_name, '$').?; // TODO error rather than a panic
2466 const segname = actual_name[0..sep];2243 const segname = actual_name[0..sep];
2467 const sectname = actual_name[sep + 1 ..];2244 const sectname = actual_name[sep + 1 ..];
2468 if (self.getSectionByName(segname, sectname)) |sect_id| {2245 if (self.getSectionByName(segname, sectname)) |sect_id| {
2469 const sect = self.sections.items(.header)[sect_id];2246 const sect = self.sections.items(.header)[sect_id];
2470 sym.value = sect.addr + sect.size;2247 sym.value = sect.addr + sect.size;
2471 sym.out_n_sect = sect_id;2248 sym.out_n_sect = sect_id;
2472 }2249 }
2473 } else unreachable;2250 } else unreachable;
2474 }2251 }
24752252
2476 if (self.objc_stubs.symbols.items.len > 0) {2253 if (self.objc_stubs.symbols.items.len > 0) {
2477 const addr = self.sections.items(.header)[self.objc_stubs_sect_index.?].addr;2254 const addr = self.sections.items(.header)[self.objc_stubs_sect_index.?].addr;
24782255
2479 for (self.objc_stubs.symbols.items, 0..) |sym_index, idx| {2256 for (self.objc_stubs.symbols.items, 0..) |ref, idx| {
2480 const sym = self.getSymbol(sym_index);2257 const sym = ref.getSymbol(self).?;
2481 sym.value = addr + idx * ObjcStubsSection.entrySize(self.getTarget().cpu.arch);2258 sym.value = addr + idx * ObjcStubsSection.entrySize(self.getTarget().cpu.arch);
2482 sym.out_n_sect = self.objc_stubs_sect_index.?;2259 sym.out_n_sect = self.objc_stubs_sect_index.?;
2260 }
2483 }2261 }
2484 }2262 }
2485}2263}
...@@ -2497,269 +2275,228 @@ fn allocateLinkeditSegment(self: *MachO) !void {...@@ -2497,269 +2275,228 @@ fn allocateLinkeditSegment(self: *MachO) !void {
2497 const seg = self.getLinkeditSegment();2275 const seg = self.getLinkeditSegment();
2498 seg.vmaddr = mem.alignForward(u64, vmaddr, page_size);2276 seg.vmaddr = mem.alignForward(u64, vmaddr, page_size);
2499 seg.fileoff = mem.alignForward(u64, fileoff, page_size);2277 seg.fileoff = mem.alignForward(u64, fileoff, page_size);
2500}
2501
2502fn initDyldInfoSections(self: *MachO) !void {
2503 const tracy = trace(@src());
2504 defer tracy.end();
2505
2506 const gpa = self.base.comp.gpa;
25072278
2508 if (self.zig_got_sect_index != null) try self.zig_got.addDyldRelocs(self);2279 var off = math.cast(u32, seg.fileoff) orelse return error.Overflow;
2509 if (self.got_sect_index != null) try self.got.addDyldRelocs(self);2280 // DYLD_INFO_ONLY
2510 if (self.tlv_ptr_sect_index != null) try self.tlv_ptr.addDyldRelocs(self);2281 {
2511 if (self.la_symbol_ptr_sect_index != null) try self.la_symbol_ptr.addDyldRelocs(self);2282 const cmd = &self.dyld_info_cmd;
2512 try self.initExportTrie();2283 cmd.rebase_off = off;
25132284 off += cmd.rebase_size;
2514 var objects = try std.ArrayList(File.Index).initCapacity(gpa, self.objects.items.len + 1);2285 cmd.bind_off = off;
2515 defer objects.deinit();2286 off += cmd.bind_size;
2516 if (self.getZigObject()) |zo| objects.appendAssumeCapacity(zo.index);2287 cmd.weak_bind_off = off;
2517 objects.appendSliceAssumeCapacity(self.objects.items);2288 off += cmd.weak_bind_size;
25182289 cmd.lazy_bind_off = off;
2519 var nrebases: usize = 0;2290 off += cmd.lazy_bind_size;
2520 var nbinds: usize = 0;2291 cmd.export_off = off;
2521 var nweak_binds: usize = 0;2292 off += cmd.export_size;
2522 for (objects.items) |index| {2293 off = mem.alignForward(u32, off, @alignOf(u64));
2523 const ctx = switch (self.getFile(index).?) {2294 }
2524 .zig_object => |x| x.dynamic_relocs,2295
2525 .object => |x| x.dynamic_relocs,2296 // FUNCTION_STARTS
2526 else => unreachable,2297 {
2527 };2298 const cmd = &self.function_starts_cmd;
2528 nrebases += ctx.rebase_relocs;2299 cmd.dataoff = off;
2529 nbinds += ctx.bind_relocs;2300 off += cmd.datasize;
2530 nweak_binds += ctx.weak_bind_relocs;2301 off = mem.alignForward(u32, off, @alignOf(u64));
2531 }
2532 if (self.getInternalObject()) |int| {
2533 nrebases += int.num_rebase_relocs;
2534 }2302 }
2535 try self.rebase.entries.ensureUnusedCapacity(gpa, nrebases);
2536 try self.bind.entries.ensureUnusedCapacity(gpa, nbinds);
2537 try self.weak_bind.entries.ensureUnusedCapacity(gpa, nweak_binds);
2538}
2539
2540fn initExportTrie(self: *MachO) !void {
2541 const tracy = trace(@src());
2542 defer tracy.end();
25432303
2544 const gpa = self.base.comp.gpa;2304 // DATA_IN_CODE
2545 try self.export_trie.init(gpa);2305 {
2306 const cmd = &self.data_in_code_cmd;
2307 cmd.dataoff = off;
2308 off += cmd.datasize;
2309 off = mem.alignForward(u32, off, @alignOf(u64));
2310 }
25462311
2547 const seg = self.getTextSegment();2312 // SYMTAB (symtab)
2548 for (self.objects.items) |index| {2313 {
2549 for (self.getFile(index).?.getSymbols()) |sym_index| {2314 const cmd = &self.symtab_cmd;
2550 const sym = self.getSymbol(sym_index);2315 cmd.symoff = off;
2551 if (!sym.flags.@"export") continue;2316 off += cmd.nsyms * @sizeOf(macho.nlist_64);
2552 if (sym.getAtom(self)) |atom| if (!atom.flags.alive) continue;2317 off = mem.alignForward(u32, off, @alignOf(u32));
2553 if (sym.getFile(self).?.getIndex() != index) continue;
2554 var flags: u64 = if (sym.flags.abs)
2555 macho.EXPORT_SYMBOL_FLAGS_KIND_ABSOLUTE
2556 else if (sym.flags.tlv)
2557 macho.EXPORT_SYMBOL_FLAGS_KIND_THREAD_LOCAL
2558 else
2559 macho.EXPORT_SYMBOL_FLAGS_KIND_REGULAR;
2560 if (sym.flags.weak) {
2561 flags |= macho.EXPORT_SYMBOL_FLAGS_WEAK_DEFINITION;
2562 self.weak_defines = true;
2563 self.binds_to_weak = true;
2564 }
2565 try self.export_trie.put(gpa, .{
2566 .name = sym.getName(self),
2567 .vmaddr_offset = sym.getAddress(.{ .stubs = false }, self) - seg.vmaddr,
2568 .export_flags = flags,
2569 });
2570 }
2571 }2318 }
25722319
2573 if (self.mh_execute_header_index) |index| {2320 // DYSYMTAB
2574 const sym = self.getSymbol(index);2321 {
2575 try self.export_trie.put(gpa, .{2322 const cmd = &self.dysymtab_cmd;
2576 .name = sym.getName(self),2323 cmd.indirectsymoff = off;
2577 .vmaddr_offset = sym.getAddress(.{}, self) - seg.vmaddr,2324 off += cmd.nindirectsyms * @sizeOf(u32);
2578 .export_flags = macho.EXPORT_SYMBOL_FLAGS_KIND_REGULAR,2325 off = mem.alignForward(u32, off, @alignOf(u64));
2579 });
2580 }2326 }
2581}
25822327
2583fn writeAtoms(self: *MachO) !void {2328 // SYMTAB (strtab)
2584 const tracy = trace(@src());2329 {
2585 defer tracy.end();2330 const cmd = &self.symtab_cmd;
2331 cmd.stroff = off;
2332 off += cmd.strsize;
2333 }
25862334
2587 const gpa = self.base.comp.gpa;2335 seg.filesize = off - seg.fileoff;
2588 var arena = std.heap.ArenaAllocator.init(gpa);2336}
2589 defer arena.deinit();
25902337
2591 const cpu_arch = self.getTarget().cpu.arch;2338fn resizeSections(self: *MachO) !void {
2592 const slice = self.sections.slice();2339 const slice = self.sections.slice();
25932340 for (slice.items(.header), slice.items(.out), 0..) |header, *out, n_sect| {
2594 var has_resolve_error = false;
2595 for (slice.items(.header), slice.items(.atoms)) |header, atoms| {
2596 if (atoms.items.len == 0) continue;
2597 if (header.isZerofill()) continue;2341 if (header.isZerofill()) continue;
25982342 if (self.isZigSection(@intCast(n_sect))) continue; // TODO this is horrible
2343 const cpu_arch = self.getTarget().cpu.arch;
2599 const size = math.cast(usize, header.size) orelse return error.Overflow;2344 const size = math.cast(usize, header.size) orelse return error.Overflow;
2600 const buffer = try gpa.alloc(u8, size);2345 try out.resize(self.base.comp.gpa, size);
2601 defer gpa.free(buffer);
2602 const padding_byte: u8 = if (header.isCode() and cpu_arch == .x86_64) 0xcc else 0;2346 const padding_byte: u8 = if (header.isCode() and cpu_arch == .x86_64) 0xcc else 0;
2603 @memset(buffer, padding_byte);2347 @memset(out.items, padding_byte);
2604
2605 for (atoms.items) |atom_index| {
2606 const atom = self.getAtom(atom_index).?;
2607 assert(atom.flags.alive);
2608 const off = math.cast(usize, atom.value) orelse return error.Overflow;
2609 const atom_size = math.cast(usize, atom.size) orelse return error.Overflow;
2610 try atom.getData(self, buffer[off..][0..atom_size]);
2611 atom.resolveRelocs(self, buffer[off..][0..atom_size]) catch |err| switch (err) {
2612 error.ResolveFailed => has_resolve_error = true,
2613 else => |e| return e,
2614 };
2615 }
2616
2617 try self.base.file.?.pwriteAll(buffer, header.offset);
2618 }2348 }
2349}
2350
2351fn writeSectionsAndUpdateLinkeditSizes(self: *MachO) !void {
2352 const gpa = self.base.comp.gpa;
26192353
2354 const cmd = self.symtab_cmd;
2355 try self.symtab.resize(gpa, cmd.nsyms);
2356 try self.strtab.resize(gpa, cmd.strsize);
2357 self.strtab.items[0] = 0;
2358
2359 for (self.objects.items) |index| {
2360 try self.getFile(index).?.writeAtoms(self);
2361 }
2362 if (self.getZigObject()) |zo| {
2363 try zo.writeAtoms(self);
2364 }
2365 if (self.getInternalObject()) |obj| {
2366 try obj.asFile().writeAtoms(self);
2367 }
2620 for (self.thunks.items) |thunk| {2368 for (self.thunks.items) |thunk| {
2621 const header = slice.items(.header)[thunk.out_n_sect];2369 const out = self.sections.items(.out)[thunk.out_n_sect].items;
2622 const offset = thunk.value + header.offset;2370 const off = math.cast(usize, thunk.value) orelse return error.Overflow;
2623 const buffer = try gpa.alloc(u8, thunk.size());2371 const size = thunk.size();
2624 defer gpa.free(buffer);2372 var stream = std.io.fixedBufferStream(out[off..][0..size]);
2625 var stream = std.io.fixedBufferStream(buffer);
2626 try thunk.write(self, stream.writer());2373 try thunk.write(self, stream.writer());
2627 try self.base.file.?.pwriteAll(buffer, offset);
2628 }2374 }
26292375
2630 if (has_resolve_error) return error.ResolveFailed;2376 const slice = self.sections.slice();
2631}2377 for (&[_]?u8{
2378 self.eh_frame_sect_index,
2379 self.unwind_info_sect_index,
2380 self.got_sect_index,
2381 self.stubs_sect_index,
2382 self.la_symbol_ptr_sect_index,
2383 self.tlv_ptr_sect_index,
2384 self.objc_stubs_sect_index,
2385 }) |maybe_sect_id| {
2386 if (maybe_sect_id) |sect_id| {
2387 const out = slice.items(.out)[sect_id].items;
2388 try self.writeSyntheticSection(sect_id, out);
2389 }
2390 }
26322391
2633fn writeUnwindInfo(self: *MachO) !void {2392 if (self.la_symbol_ptr_sect_index) |_| {
2634 const tracy = trace(@src());2393 try self.updateLazyBindSize();
2635 defer tracy.end();2394 }
26362395
2637 const gpa = self.base.comp.gpa;2396 try self.rebase.updateSize(self);
2397 try self.bind.updateSize(self);
2398 try self.weak_bind.updateSize(self);
2399 try self.export_trie.updateSize(self);
2400 try self.data_in_code.updateSize(self);
26382401
2639 if (self.eh_frame_sect_index) |index| {2402 if (self.getZigObject()) |zo| {
2640 const header = self.sections.items(.header)[index];2403 zo.asFile().writeSymtab(self, self);
2641 const size = math.cast(usize, header.size) orelse return error.Overflow;
2642 const buffer = try gpa.alloc(u8, size);
2643 defer gpa.free(buffer);
2644 eh_frame.write(self, buffer);
2645 try self.base.file.?.pwriteAll(buffer, header.offset);
2646 }2404 }
26472405 for (self.objects.items) |index| {
2648 if (self.unwind_info_sect_index) |index| {2406 self.getFile(index).?.writeSymtab(self, self);
2649 const header = self.sections.items(.header)[index];2407 }
2650 const size = math.cast(usize, header.size) orelse return error.Overflow;2408 for (self.dylibs.items) |index| {
2651 const buffer = try gpa.alloc(u8, size);2409 self.getFile(index).?.writeSymtab(self, self);
2652 defer gpa.free(buffer);2410 }
2653 try self.unwind_info.write(self, buffer);2411 if (self.getInternalObject()) |obj| {
2654 try self.base.file.?.pwriteAll(buffer, header.offset);2412 obj.asFile().writeSymtab(self, self);
2655 }2413 }
2656}2414}
26572415
2658fn finalizeDyldInfoSections(self: *MachO) !void {2416fn writeSyntheticSection(self: *MachO, sect_id: u8, out: []u8) !void {
2659 const tracy = trace(@src());2417 const tracy = trace(@src());
2660 defer tracy.end();2418 defer tracy.end();
2661 const gpa = self.base.comp.gpa;
26622419
2663 try self.rebase.finalize(gpa);2420 const Tag = enum {
2664 try self.bind.finalize(gpa, self);2421 eh_frame,
2665 try self.weak_bind.finalize(gpa, self);2422 unwind_info,
2666 try self.lazy_bind.finalize(gpa, self);2423 got,
2667 try self.export_trie.finalize(gpa);2424 stubs,
2425 la_symbol_ptr,
2426 tlv_ptr,
2427 objc_stubs,
2428 };
2429
2430 const tag: Tag = tag: {
2431 if (self.eh_frame_sect_index != null and
2432 self.eh_frame_sect_index.? == sect_id) break :tag .eh_frame;
2433 if (self.unwind_info_sect_index != null and
2434 self.unwind_info_sect_index.? == sect_id) break :tag .unwind_info;
2435 if (self.got_sect_index != null and
2436 self.got_sect_index.? == sect_id) break :tag .got;
2437 if (self.stubs_sect_index != null and
2438 self.stubs_sect_index.? == sect_id) break :tag .stubs;
2439 if (self.la_symbol_ptr_sect_index != null and
2440 self.la_symbol_ptr_sect_index.? == sect_id) break :tag .la_symbol_ptr;
2441 if (self.tlv_ptr_sect_index != null and
2442 self.tlv_ptr_sect_index.? == sect_id) break :tag .tlv_ptr;
2443 if (self.objc_stubs_sect_index != null and
2444 self.objc_stubs_sect_index.? == sect_id) break :tag .objc_stubs;
2445 unreachable;
2446 };
2447 var stream = std.io.fixedBufferStream(out);
2448 switch (tag) {
2449 .eh_frame => eh_frame.write(self, out),
2450 .unwind_info => try self.unwind_info.write(self, out),
2451 .got => try self.got.write(self, stream.writer()),
2452 .stubs => try self.stubs.write(self, stream.writer()),
2453 .la_symbol_ptr => try self.la_symbol_ptr.write(self, stream.writer()),
2454 .tlv_ptr => try self.tlv_ptr.write(self, stream.writer()),
2455 .objc_stubs => try self.objc_stubs.write(self, stream.writer()),
2456 }
2668}2457}
26692458
2670fn writeSyntheticSections(self: *MachO) !void {2459fn updateLazyBindSize(self: *MachO) !void {
2671 const tracy = trace(@src());2460 const tracy = trace(@src());
2672 defer tracy.end();2461 defer tracy.end();
2462 try self.lazy_bind.updateSize(self);
2463 const sect_id = self.stubs_helper_sect_index.?;
2464 const out = &self.sections.items(.out)[sect_id];
2465 var stream = std.io.fixedBufferStream(out.items);
2466 try self.stubs_helper.write(self, stream.writer());
2467}
26732468
2674 const gpa = self.base.comp.gpa;2469fn writeSectionsToFile(self: *MachO) !void {
26752470 const tracy = trace(@src());
2676 if (self.got_sect_index) |sect_id| {2471 defer tracy.end();
2677 const header = self.sections.items(.header)[sect_id];
2678 const size = math.cast(usize, header.size) orelse return error.Overflow;
2679 var buffer = try std.ArrayList(u8).initCapacity(gpa, size);
2680 defer buffer.deinit();
2681 try self.got.write(self, buffer.writer());
2682 assert(buffer.items.len == header.size);
2683 try self.base.file.?.pwriteAll(buffer.items, header.offset);
2684 }
2685
2686 if (self.stubs_sect_index) |sect_id| {
2687 const header = self.sections.items(.header)[sect_id];
2688 const size = math.cast(usize, header.size) orelse return error.Overflow;
2689 var buffer = try std.ArrayList(u8).initCapacity(gpa, size);
2690 defer buffer.deinit();
2691 try self.stubs.write(self, buffer.writer());
2692 assert(buffer.items.len == header.size);
2693 try self.base.file.?.pwriteAll(buffer.items, header.offset);
2694 }
2695
2696 if (self.stubs_helper_sect_index) |sect_id| {
2697 const header = self.sections.items(.header)[sect_id];
2698 const size = math.cast(usize, header.size) orelse return error.Overflow;
2699 var buffer = try std.ArrayList(u8).initCapacity(gpa, size);
2700 defer buffer.deinit();
2701 try self.stubs_helper.write(self, buffer.writer());
2702 assert(buffer.items.len == header.size);
2703 try self.base.file.?.pwriteAll(buffer.items, header.offset);
2704 }
2705
2706 if (self.la_symbol_ptr_sect_index) |sect_id| {
2707 const header = self.sections.items(.header)[sect_id];
2708 const size = math.cast(usize, header.size) orelse return error.Overflow;
2709 var buffer = try std.ArrayList(u8).initCapacity(gpa, size);
2710 defer buffer.deinit();
2711 try self.la_symbol_ptr.write(self, buffer.writer());
2712 assert(buffer.items.len == header.size);
2713 try self.base.file.?.pwriteAll(buffer.items, header.offset);
2714 }
27152472
2716 if (self.tlv_ptr_sect_index) |sect_id| {2473 const slice = self.sections.slice();
2717 const header = self.sections.items(.header)[sect_id];2474 for (slice.items(.header), slice.items(.out)) |header, out| {
2718 const size = math.cast(usize, header.size) orelse return error.Overflow;2475 try self.base.file.?.pwriteAll(out.items, header.offset);
2719 var buffer = try std.ArrayList(u8).initCapacity(gpa, size);
2720 defer buffer.deinit();
2721 try self.tlv_ptr.write(self, buffer.writer());
2722 assert(buffer.items.len == header.size);
2723 try self.base.file.?.pwriteAll(buffer.items, header.offset);
2724 }2476 }
2477}
27252478
2726 if (self.objc_stubs_sect_index) |sect_id| {2479fn writeLinkeditSectionsToFile(self: *MachO) !void {
2727 const header = self.sections.items(.header)[sect_id];2480 const tracy = trace(@src());
2728 const size = math.cast(usize, header.size) orelse return error.Overflow;2481 defer tracy.end();
2729 var buffer = try std.ArrayList(u8).initCapacity(gpa, size);2482 try self.writeDyldInfo();
2730 defer buffer.deinit();2483 try self.writeDataInCode();
2731 try self.objc_stubs.write(self, buffer.writer());2484 try self.writeSymtabToFile();
2732 assert(buffer.items.len == header.size);2485 try self.writeIndsymtab();
2733 try self.base.file.?.pwriteAll(buffer.items, header.offset);
2734 }
2735}2486}
27362487
2737fn writeDyldInfoSections(self: *MachO, off: u32) !u32 {2488fn writeDyldInfo(self: *MachO) !void {
2738 const tracy = trace(@src());2489 const tracy = trace(@src());
2739 defer tracy.end();2490 defer tracy.end();
27402491
2741 const gpa = self.base.comp.gpa;2492 const gpa = self.base.comp.gpa;
2742 const cmd = &self.dyld_info_cmd;2493 const base_off = self.getLinkeditSegment().fileoff;
2494 const cmd = self.dyld_info_cmd;
2743 var needed_size: u32 = 0;2495 var needed_size: u32 = 0;
2744
2745 cmd.rebase_off = needed_size;
2746 cmd.rebase_size = mem.alignForward(u32, @intCast(self.rebase.size()), @alignOf(u64));
2747 needed_size += cmd.rebase_size;2496 needed_size += cmd.rebase_size;
2748
2749 cmd.bind_off = needed_size;
2750 cmd.bind_size = mem.alignForward(u32, @intCast(self.bind.size()), @alignOf(u64));
2751 needed_size += cmd.bind_size;2497 needed_size += cmd.bind_size;
2752
2753 cmd.weak_bind_off = needed_size;
2754 cmd.weak_bind_size = mem.alignForward(u32, @intCast(self.weak_bind.size()), @alignOf(u64));
2755 needed_size += cmd.weak_bind_size;2498 needed_size += cmd.weak_bind_size;
2756
2757 cmd.lazy_bind_off = needed_size;
2758 cmd.lazy_bind_size = mem.alignForward(u32, @intCast(self.lazy_bind.size()), @alignOf(u64));
2759 needed_size += cmd.lazy_bind_size;2499 needed_size += cmd.lazy_bind_size;
2760
2761 cmd.export_off = needed_size;
2762 cmd.export_size = mem.alignForward(u32, @intCast(self.export_trie.size), @alignOf(u64));
2763 needed_size += cmd.export_size;2500 needed_size += cmd.export_size;
27642501
2765 const buffer = try gpa.alloc(u8, needed_size);2502 const buffer = try gpa.alloc(u8, needed_size);
...@@ -2770,89 +2507,78 @@ fn writeDyldInfoSections(self: *MachO, off: u32) !u32 {...@@ -2770,89 +2507,78 @@ fn writeDyldInfoSections(self: *MachO, off: u32) !u32 {
2770 const writer = stream.writer();2507 const writer = stream.writer();
27712508
2772 try self.rebase.write(writer);2509 try self.rebase.write(writer);
2773 try stream.seekTo(cmd.bind_off);2510 try stream.seekTo(cmd.bind_off - base_off);
2774 try self.bind.write(writer);2511 try self.bind.write(writer);
2775 try stream.seekTo(cmd.weak_bind_off);2512 try stream.seekTo(cmd.weak_bind_off - base_off);
2776 try self.weak_bind.write(writer);2513 try self.weak_bind.write(writer);
2777 try stream.seekTo(cmd.lazy_bind_off);2514 try stream.seekTo(cmd.lazy_bind_off - base_off);
2778 try self.lazy_bind.write(writer);2515 try self.lazy_bind.write(writer);
2779 try stream.seekTo(cmd.export_off);2516 try stream.seekTo(cmd.export_off - base_off);
2780 try self.export_trie.write(writer);2517 try self.export_trie.write(writer);
2518 try self.base.file.?.pwriteAll(buffer, cmd.rebase_off);
2519}
27812520
2782 cmd.rebase_off += off;2521pub fn writeDataInCode(self: *MachO) !void {
2783 cmd.bind_off += off;2522 const tracy = trace(@src());
2784 cmd.weak_bind_off += off;2523 defer tracy.end();
2785 cmd.lazy_bind_off += off;2524 const gpa = self.base.comp.gpa;
2786 cmd.export_off += off;2525 const cmd = self.data_in_code_cmd;
27872526 var buffer = try std.ArrayList(u8).initCapacity(gpa, self.data_in_code.size());
2788 try self.base.file.?.pwriteAll(buffer, off);2527 defer buffer.deinit();
2528 try self.data_in_code.write(self, buffer.writer());
2529 try self.base.file.?.pwriteAll(buffer.items, cmd.dataoff);
2530}
27892531
2790 return off + needed_size;2532fn writeIndsymtab(self: *MachO) !void {
2533 const tracy = trace(@src());
2534 defer tracy.end();
2535 const gpa = self.base.comp.gpa;
2536 const cmd = self.dysymtab_cmd;
2537 const needed_size = cmd.nindirectsyms * @sizeOf(u32);
2538 var buffer = try std.ArrayList(u8).initCapacity(gpa, needed_size);
2539 defer buffer.deinit();
2540 try self.indsymtab.write(self, buffer.writer());
2541 try self.base.file.?.pwriteAll(buffer.items, cmd.indirectsymoff);
2791}2542}
27922543
2793fn writeFunctionStarts(self: *MachO, off: u32) !u32 {2544pub fn writeSymtabToFile(self: *MachO) !void {
2794 // TODO actually write it out2545 const tracy = trace(@src());
2795 const cmd = &self.function_starts_cmd;2546 defer tracy.end();
2796 cmd.dataoff = off;2547 const cmd = self.symtab_cmd;
2797 return off;2548 try self.base.file.?.pwriteAll(mem.sliceAsBytes(self.symtab.items), cmd.symoff);
2549 try self.base.file.?.pwriteAll(self.strtab.items, cmd.stroff);
2798}2550}
27992551
2800pub fn writeDataInCode(self: *MachO, base_address: u64, off: u32) !u32 {2552fn writeUnwindInfo(self: *MachO) !void {
2801 const cmd = &self.data_in_code_cmd;2553 const tracy = trace(@src());
2802 cmd.dataoff = off;2554 defer tracy.end();
28032555
2804 const gpa = self.base.comp.gpa;2556 const gpa = self.base.comp.gpa;
2805 var dices = std.ArrayList(macho.data_in_code_entry).init(gpa);
2806 defer dices.deinit();
28072557
2808 for (self.objects.items) |index| {2558 if (self.eh_frame_sect_index) |index| {
2809 const object = self.getFile(index).?.object;2559 const header = self.sections.items(.header)[index];
2810 const in_dices = object.getDataInCode();2560 const size = math.cast(usize, header.size) orelse return error.Overflow;
28112561 const buffer = try gpa.alloc(u8, size);
2812 try dices.ensureUnusedCapacity(in_dices.len);2562 defer gpa.free(buffer);
28132563 eh_frame.write(self, buffer);
2814 var next_dice: usize = 0;2564 try self.base.file.?.pwriteAll(buffer, header.offset);
2815 for (object.atoms.items) |atom_index| {
2816 if (next_dice >= in_dices.len) break;
2817 const atom = self.getAtom(atom_index) orelse continue;
2818 const start_off = atom.getInputAddress(self);
2819 const end_off = start_off + atom.size;
2820 const start_dice = next_dice;
2821
2822 if (end_off < in_dices[next_dice].offset) continue;
2823
2824 while (next_dice < in_dices.len and
2825 in_dices[next_dice].offset < end_off) : (next_dice += 1)
2826 {}
2827
2828 if (atom.flags.alive) for (in_dices[start_dice..next_dice]) |dice| {
2829 dices.appendAssumeCapacity(.{
2830 .offset = @intCast(atom.getAddress(self) + dice.offset - start_off - base_address),
2831 .length = dice.length,
2832 .kind = dice.kind,
2833 });
2834 };
2835 }
2836 }2565 }
28372566
2838 const needed_size = math.cast(u32, dices.items.len * @sizeOf(macho.data_in_code_entry)) orelse return error.Overflow;2567 if (self.unwind_info_sect_index) |index| {
2839 cmd.datasize = needed_size;2568 const header = self.sections.items(.header)[index];
28402569 const size = math.cast(usize, header.size) orelse return error.Overflow;
2841 try self.base.file.?.pwriteAll(mem.sliceAsBytes(dices.items), cmd.dataoff);2570 const buffer = try gpa.alloc(u8, size);
28422571 defer gpa.free(buffer);
2843 return off + needed_size;2572 try self.unwind_info.write(self, buffer);
2573 try self.base.file.?.pwriteAll(buffer, header.offset);
2574 }
2844}2575}
28452576
2846pub fn calcSymtabSize(self: *MachO) !void {2577fn calcSymtabSize(self: *MachO) !void {
2847 const tracy = trace(@src());2578 const tracy = trace(@src());
2848 defer tracy.end();2579 defer tracy.end();
2849 const gpa = self.base.comp.gpa;
28502580
2851 var nlocals: u32 = 0;2581 const gpa = self.base.comp.gpa;
2852 var nstabs: u32 = 0;
2853 var nexports: u32 = 0;
2854 var nimports: u32 = 0;
2855 var strsize: u32 = 0;
28562582
2857 var files = std.ArrayList(File.Index).init(gpa);2583 var files = std.ArrayList(File.Index).init(gpa);
2858 defer files.deinit();2584 defer files.deinit();
...@@ -2862,6 +2588,12 @@ pub fn calcSymtabSize(self: *MachO) !void {...@@ -2862,6 +2588,12 @@ pub fn calcSymtabSize(self: *MachO) !void {
2862 for (self.dylibs.items) |index| files.appendAssumeCapacity(index);2588 for (self.dylibs.items) |index| files.appendAssumeCapacity(index);
2863 if (self.internal_object) |index| files.appendAssumeCapacity(index);2589 if (self.internal_object) |index| files.appendAssumeCapacity(index);
28642590
2591 var nlocals: u32 = 0;
2592 var nstabs: u32 = 0;
2593 var nexports: u32 = 0;
2594 var nimports: u32 = 0;
2595 var strsize: u32 = 1;
2596
2865 for (files.items) |index| {2597 for (files.items) |index| {
2866 const file = self.getFile(index).?;2598 const file = self.getFile(index).?;
2867 const ctx = switch (file) {2599 const ctx = switch (file) {
...@@ -2871,7 +2603,7 @@ pub fn calcSymtabSize(self: *MachO) !void {...@@ -2871,7 +2603,7 @@ pub fn calcSymtabSize(self: *MachO) !void {
2871 ctx.istab = nstabs;2603 ctx.istab = nstabs;
2872 ctx.iexport = nexports;2604 ctx.iexport = nexports;
2873 ctx.iimport = nimports;2605 ctx.iimport = nimports;
2874 try file.calcSymtabSize(self);2606 ctx.stroff = strsize;
2875 nlocals += ctx.nlocals;2607 nlocals += ctx.nlocals;
2876 nstabs += ctx.nstabs;2608 nstabs += ctx.nstabs;
2877 nexports += ctx.nexports;2609 nexports += ctx.nexports;
...@@ -2889,10 +2621,12 @@ pub fn calcSymtabSize(self: *MachO) !void {...@@ -2889,10 +2621,12 @@ pub fn calcSymtabSize(self: *MachO) !void {
2889 ctx.iimport += nlocals + nstabs + nexports;2621 ctx.iimport += nlocals + nstabs + nexports;
2890 }2622 }
28912623
2624 try self.indsymtab.updateSize(self);
2625
2892 {2626 {
2893 const cmd = &self.symtab_cmd;2627 const cmd = &self.symtab_cmd;
2894 cmd.nsyms = nlocals + nstabs + nexports + nimports;2628 cmd.nsyms = nlocals + nstabs + nexports + nimports;
2895 cmd.strsize = strsize + 1;2629 cmd.strsize = strsize;
2896 }2630 }
28972631
2898 {2632 {
...@@ -2906,60 +2640,6 @@ pub fn calcSymtabSize(self: *MachO) !void {...@@ -2906,60 +2640,6 @@ pub fn calcSymtabSize(self: *MachO) !void {
2906 }2640 }
2907}2641}
29082642
2909pub fn writeSymtab(self: *MachO, off: u32) !u32 {
2910 const tracy = trace(@src());
2911 defer tracy.end();
2912 const gpa = self.base.comp.gpa;
2913 const cmd = &self.symtab_cmd;
2914 cmd.symoff = off;
2915
2916 try self.symtab.resize(gpa, cmd.nsyms);
2917 try self.strtab.ensureUnusedCapacity(gpa, cmd.strsize - 1);
2918
2919 if (self.getZigObject()) |zo| {
2920 zo.writeSymtab(self, self);
2921 }
2922 for (self.objects.items) |index| {
2923 try self.getFile(index).?.writeSymtab(self, self);
2924 }
2925 for (self.dylibs.items) |index| {
2926 try self.getFile(index).?.writeSymtab(self, self);
2927 }
2928 if (self.getInternalObject()) |internal| {
2929 internal.writeSymtab(self, self);
2930 }
2931
2932 assert(self.strtab.items.len == cmd.strsize);
2933
2934 try self.base.file.?.pwriteAll(mem.sliceAsBytes(self.symtab.items), cmd.symoff);
2935
2936 return off + cmd.nsyms * @sizeOf(macho.nlist_64);
2937}
2938
2939fn writeIndsymtab(self: *MachO, off: u32) !u32 {
2940 const gpa = self.base.comp.gpa;
2941 const cmd = &self.dysymtab_cmd;
2942 cmd.indirectsymoff = off;
2943 cmd.nindirectsyms = self.indsymtab.nsyms(self);
2944
2945 const needed_size = cmd.nindirectsyms * @sizeOf(u32);
2946 var buffer = try std.ArrayList(u8).initCapacity(gpa, needed_size);
2947 defer buffer.deinit();
2948 try self.indsymtab.write(self, buffer.writer());
2949
2950 try self.base.file.?.pwriteAll(buffer.items, cmd.indirectsymoff);
2951 assert(buffer.items.len == needed_size);
2952
2953 return off + needed_size;
2954}
2955
2956pub fn writeStrtab(self: *MachO, off: u32) !u32 {
2957 const cmd = &self.symtab_cmd;
2958 cmd.stroff = off;
2959 try self.base.file.?.pwriteAll(self.strtab.items, cmd.stroff);
2960 return off + cmd.strsize;
2961}
2962
2963fn writeLoadCommands(self: *MachO) !struct { usize, usize, u64 } {2643fn writeLoadCommands(self: *MachO) !struct { usize, usize, u64 } {
2964 const comp = self.base.comp;2644 const comp = self.base.comp;
2965 const gpa = comp.gpa;2645 const gpa = comp.gpa;
...@@ -2999,18 +2679,20 @@ fn writeLoadCommands(self: *MachO) !struct { usize, usize, u64 } {...@@ -2999,18 +2679,20 @@ fn writeLoadCommands(self: *MachO) !struct { usize, usize, u64 } {
2999 try load_commands.writeDylinkerLC(writer);2679 try load_commands.writeDylinkerLC(writer);
3000 ncmds += 1;2680 ncmds += 1;
30012681
3002 if (self.entry_index) |global_index| {2682 if (self.getInternalObject()) |obj| {
3003 const sym = self.getSymbol(global_index);2683 if (obj.getEntryRef(self)) |ref| {
3004 const seg = self.getTextSegment();2684 const sym = ref.getSymbol(self).?;
3005 const entryoff: u32 = if (sym.getFile(self) == null)2685 const seg = self.getTextSegment();
3006 02686 const entryoff: u32 = if (sym.getFile(self) == null)
3007 else2687 0
3008 @as(u32, @intCast(sym.getAddress(.{ .stubs = true }, self) - seg.vmaddr));2688 else
3009 try writer.writeStruct(macho.entry_point_command{2689 @as(u32, @intCast(sym.getAddress(.{ .stubs = true }, self) - seg.vmaddr));
3010 .entryoff = entryoff,2690 try writer.writeStruct(macho.entry_point_command{
3011 .stacksize = self.base.stack_size,2691 .entryoff = entryoff,
3012 });2692 .stacksize = self.base.stack_size,
3013 ncmds += 1;2693 });
2694 ncmds += 1;
2695 }
3014 }2696 }
30152697
3016 if (self.base.isDynLib()) {2698 if (self.base.isDynLib()) {
...@@ -3919,157 +3601,6 @@ pub fn getFileHandle(self: MachO, index: File.HandleIndex) File.Handle {...@@ -3919,157 +3601,6 @@ pub fn getFileHandle(self: MachO, index: File.HandleIndex) File.Handle {
3919 return self.file_handles.items[index];3601 return self.file_handles.items[index];
3920}3602}
39213603
3922pub fn addAtom(self: *MachO) error{OutOfMemory}!Atom.Index {
3923 const index = @as(Atom.Index, @intCast(self.atoms.items.len));
3924 const atom = try self.atoms.addOne(self.base.comp.gpa);
3925 atom.* = .{};
3926 return index;
3927}
3928
3929pub fn getAtom(self: *MachO, index: Atom.Index) ?*Atom {
3930 if (index == 0) return null;
3931 assert(index < self.atoms.items.len);
3932 return &self.atoms.items[index];
3933}
3934
3935pub fn addAtomExtra(self: *MachO, extra: Atom.Extra) !u32 {
3936 const fields = @typeInfo(Atom.Extra).Struct.fields;
3937 try self.atoms_extra.ensureUnusedCapacity(self.base.comp.gpa, fields.len);
3938 return self.addAtomExtraAssumeCapacity(extra);
3939}
3940
3941pub fn addAtomExtraAssumeCapacity(self: *MachO, extra: Atom.Extra) u32 {
3942 const index = @as(u32, @intCast(self.atoms_extra.items.len));
3943 const fields = @typeInfo(Atom.Extra).Struct.fields;
3944 inline for (fields) |field| {
3945 self.atoms_extra.appendAssumeCapacity(switch (field.type) {
3946 u32 => @field(extra, field.name),
3947 else => @compileError("bad field type"),
3948 });
3949 }
3950 return index;
3951}
3952
3953pub fn getAtomExtra(self: *MachO, index: u32) ?Atom.Extra {
3954 if (index == 0) return null;
3955 const fields = @typeInfo(Atom.Extra).Struct.fields;
3956 var i: usize = index;
3957 var result: Atom.Extra = undefined;
3958 inline for (fields) |field| {
3959 @field(result, field.name) = switch (field.type) {
3960 u32 => self.atoms_extra.items[i],
3961 else => @compileError("bad field type"),
3962 };
3963 i += 1;
3964 }
3965 return result;
3966}
3967
3968pub fn setAtomExtra(self: *MachO, index: u32, extra: Atom.Extra) void {
3969 assert(index > 0);
3970 const fields = @typeInfo(Atom.Extra).Struct.fields;
3971 inline for (fields, 0..) |field, i| {
3972 self.atoms_extra.items[index + i] = switch (field.type) {
3973 u32 => @field(extra, field.name),
3974 else => @compileError("bad field type"),
3975 };
3976 }
3977}
3978
3979pub fn addSymbol(self: *MachO) !Symbol.Index {
3980 const index = @as(Symbol.Index, @intCast(self.symbols.items.len));
3981 const symbol = try self.symbols.addOne(self.base.comp.gpa);
3982 symbol.* = .{};
3983 return index;
3984}
3985
3986pub fn getSymbol(self: *MachO, index: Symbol.Index) *Symbol {
3987 assert(index < self.symbols.items.len);
3988 return &self.symbols.items[index];
3989}
3990
3991pub fn addSymbolExtra(self: *MachO, extra: Symbol.Extra) !u32 {
3992 const fields = @typeInfo(Symbol.Extra).Struct.fields;
3993 try self.symbols_extra.ensureUnusedCapacity(self.base.comp.gpa, fields.len);
3994 return self.addSymbolExtraAssumeCapacity(extra);
3995}
3996
3997pub fn addSymbolExtraAssumeCapacity(self: *MachO, extra: Symbol.Extra) u32 {
3998 const index = @as(u32, @intCast(self.symbols_extra.items.len));
3999 const fields = @typeInfo(Symbol.Extra).Struct.fields;
4000 inline for (fields) |field| {
4001 self.symbols_extra.appendAssumeCapacity(switch (field.type) {
4002 u32 => @field(extra, field.name),
4003 else => @compileError("bad field type"),
4004 });
4005 }
4006 return index;
4007}
4008
4009pub fn getSymbolExtra(self: MachO, index: u32) ?Symbol.Extra {
4010 if (index == 0) return null;
4011 const fields = @typeInfo(Symbol.Extra).Struct.fields;
4012 var i: usize = index;
4013 var result: Symbol.Extra = undefined;
4014 inline for (fields) |field| {
4015 @field(result, field.name) = switch (field.type) {
4016 u32 => self.symbols_extra.items[i],
4017 else => @compileError("bad field type"),
4018 };
4019 i += 1;
4020 }
4021 return result;
4022}
4023
4024pub fn setSymbolExtra(self: *MachO, index: u32, extra: Symbol.Extra) void {
4025 assert(index > 0);
4026 const fields = @typeInfo(Symbol.Extra).Struct.fields;
4027 inline for (fields, 0..) |field, i| {
4028 self.symbols_extra.items[index + i] = switch (field.type) {
4029 u32 => @field(extra, field.name),
4030 else => @compileError("bad field type"),
4031 };
4032 }
4033}
4034
4035const GetOrCreateGlobalResult = struct {
4036 found_existing: bool,
4037 index: Symbol.Index,
4038};
4039
4040pub fn getOrCreateGlobal(self: *MachO, off: u32) !GetOrCreateGlobalResult {
4041 const gpa = self.base.comp.gpa;
4042 const gop = try self.globals.getOrPut(gpa, off);
4043 if (!gop.found_existing) {
4044 const index = try self.addSymbol();
4045 const global = self.getSymbol(index);
4046 global.name = off;
4047 global.flags.global = true;
4048 gop.value_ptr.* = index;
4049 }
4050 return .{
4051 .found_existing = gop.found_existing,
4052 .index = gop.value_ptr.*,
4053 };
4054}
4055
4056pub fn getGlobalByName(self: *MachO, name: []const u8) ?Symbol.Index {
4057 const off = self.strings.getOffset(name) orelse return null;
4058 return self.globals.get(off);
4059}
4060
4061pub fn addUnwindRecord(self: *MachO) !UnwindInfo.Record.Index {
4062 const index = @as(UnwindInfo.Record.Index, @intCast(self.unwind_records.items.len));
4063 const rec = try self.unwind_records.addOne(self.base.comp.gpa);
4064 rec.* = .{};
4065 return index;
4066}
4067
4068pub fn getUnwindRecord(self: *MachO, index: UnwindInfo.Record.Index) *UnwindInfo.Record {
4069 assert(index < self.unwind_records.items.len);
4070 return &self.unwind_records.items[index];
4071}
4072
4073pub fn addThunk(self: *MachO) !Thunk.Index {3604pub fn addThunk(self: *MachO) !Thunk.Index {
4074 const index = @as(Thunk.Index, @intCast(self.thunks.items.len));3605 const index = @as(Thunk.Index, @intCast(self.thunks.items.len));
4075 const thunk = try self.thunks.addOne(self.base.comp.gpa);3606 const thunk = try self.thunks.addOne(self.base.comp.gpa);
...@@ -4208,22 +3739,23 @@ pub fn reportUnexpectedError(self: *MachO, comptime format: []const u8, args: an...@@ -4208,22 +3739,23 @@ pub fn reportUnexpectedError(self: *MachO, comptime format: []const u8, args: an
4208 try err.addNote(self, "please report this as a linker bug on https://github.com/ziglang/zig/issues/new/choose", .{});3739 try err.addNote(self, "please report this as a linker bug on https://github.com/ziglang/zig/issues/new/choose", .{});
4209}3740}
42103741
4211fn reportDuplicates(self: *MachO, dupes: anytype) error{ HasDuplicates, OutOfMemory }!void {3742fn reportDuplicates(self: *MachO) error{ HasDuplicates, OutOfMemory }!void {
4212 const tracy = trace(@src());3743 const tracy = trace(@src());
4213 defer tracy.end();3744 defer tracy.end();
42143745
4215 const max_notes = 3;3746 const max_notes = 3;
42163747
4217 var has_dupes = false;3748 var has_dupes = false;
4218 var it = dupes.iterator();3749 var it = self.dupes.iterator();
4219 while (it.next()) |entry| {3750 while (it.next()) |entry| {
4220 const sym = self.getSymbol(entry.key_ptr.*);3751 const sym = self.resolver.keys.items[entry.key_ptr.* - 1];
4221 const notes = entry.value_ptr.*;3752 const notes = entry.value_ptr.*;
4222 const nnotes = @min(notes.items.len, max_notes) + @intFromBool(notes.items.len > max_notes);3753 const nnotes = @min(notes.items.len, max_notes) + @intFromBool(notes.items.len > max_notes);
42233754
4224 var err = try self.addErrorWithNotes(nnotes + 1);3755 var err = try self.addErrorWithNotes(nnotes + 1);
4225 try err.addMsg(self, "duplicate symbol definition: {s}", .{sym.getName(self)});3756 try err.addMsg(self, "duplicate symbol definition: {s}", .{sym.getName(self)});
4226 try err.addNote(self, "defined by {}", .{sym.getFile(self).?.fmtPath()});3757 try err.addNote(self, "defined by {}", .{sym.getFile(self).?.fmtPath()});
3758 has_dupes = true;
42273759
4228 var inote: usize = 0;3760 var inote: usize = 0;
4229 while (inote < @min(notes.items.len, max_notes)) : (inote += 1) {3761 while (inote < @min(notes.items.len, max_notes)) : (inote += 1) {
...@@ -4235,10 +3767,7 @@ fn reportDuplicates(self: *MachO, dupes: anytype) error{ HasDuplicates, OutOfMem...@@ -4235,10 +3767,7 @@ fn reportDuplicates(self: *MachO, dupes: anytype) error{ HasDuplicates, OutOfMem
4235 const remaining = notes.items.len - max_notes;3767 const remaining = notes.items.len - max_notes;
4236 try err.addNote(self, "defined {d} more times", .{remaining});3768 try err.addNote(self, "defined {d} more times", .{remaining});
4237 }3769 }
4238
4239 has_dupes = true;
4240 }3770 }
4241
4242 if (has_dupes) return error.HasDuplicates;3771 if (has_dupes) return error.HasDuplicates;
4243}3772}
42443773
...@@ -4435,15 +3964,18 @@ pub const base_tag: link.File.Tag = link.File.Tag.macho;...@@ -4435,15 +3964,18 @@ pub const base_tag: link.File.Tag = link.File.Tag.macho;
4435const Section = struct {3964const Section = struct {
4436 header: macho.section_64,3965 header: macho.section_64,
4437 segment_id: u8,3966 segment_id: u8,
4438 atoms: std.ArrayListUnmanaged(Atom.Index) = .{},3967 atoms: std.ArrayListUnmanaged(Ref) = .{},
4439 free_list: std.ArrayListUnmanaged(Atom.Index) = .{},3968 free_list: std.ArrayListUnmanaged(Atom.Index) = .{},
4440 last_atom_index: Atom.Index = 0,3969 last_atom_index: Atom.Index = 0,
3970 thunks: std.ArrayListUnmanaged(Thunk.Index) = .{},
3971 out: std.ArrayListUnmanaged(u8) = .{},
3972 relocs: std.ArrayListUnmanaged(macho.relocation_info) = .{},
4441};3973};
44423974
4443pub const LiteralPool = struct {3975pub const LiteralPool = struct {
4444 table: std.AutoArrayHashMapUnmanaged(void, void) = .{},3976 table: std.AutoArrayHashMapUnmanaged(void, void) = .{},
4445 keys: std.ArrayListUnmanaged(Key) = .{},3977 keys: std.ArrayListUnmanaged(Key) = .{},
4446 values: std.ArrayListUnmanaged(Atom.Index) = .{},3978 values: std.ArrayListUnmanaged(MachO.Ref) = .{},
4447 data: std.ArrayListUnmanaged(u8) = .{},3979 data: std.ArrayListUnmanaged(u8) = .{},
44483980
4449 pub fn deinit(lp: *LiteralPool, allocator: Allocator) void {3981 pub fn deinit(lp: *LiteralPool, allocator: Allocator) void {
...@@ -4453,17 +3985,21 @@ pub const LiteralPool = struct {...@@ -4453,17 +3985,21 @@ pub const LiteralPool = struct {
4453 lp.data.deinit(allocator);3985 lp.data.deinit(allocator);
4454 }3986 }
44553987
4456 pub fn getAtom(lp: LiteralPool, index: Index, macho_file: *MachO) *Atom {
4457 assert(index < lp.values.items.len);
4458 return macho_file.getAtom(lp.values.items[index]).?;
4459 }
4460
4461 const InsertResult = struct {3988 const InsertResult = struct {
4462 found_existing: bool,3989 found_existing: bool,
4463 index: Index,3990 index: Index,
4464 atom: *Atom.Index,3991 ref: *MachO.Ref,
4465 };3992 };
44663993
3994 pub fn getSymbolRef(lp: LiteralPool, index: Index) MachO.Ref {
3995 assert(index < lp.values.items.len);
3996 return lp.values.items[index];
3997 }
3998
3999 pub fn getSymbol(lp: LiteralPool, index: Index, macho_file: *MachO) *Symbol {
4000 return lp.getSymbolRef(index).getSymbol(macho_file).?;
4001 }
4002
4467 pub fn insert(lp: *LiteralPool, allocator: Allocator, @"type": u8, string: []const u8) !InsertResult {4003 pub fn insert(lp: *LiteralPool, allocator: Allocator, @"type": u8, string: []const u8) !InsertResult {
4468 const size: u32 = @intCast(string.len);4004 const size: u32 = @intCast(string.len);
4469 try lp.data.ensureUnusedCapacity(allocator, size);4005 try lp.data.ensureUnusedCapacity(allocator, size);
...@@ -4479,7 +4015,7 @@ pub const LiteralPool = struct {...@@ -4479,7 +4015,7 @@ pub const LiteralPool = struct {
4479 return .{4015 return .{
4480 .found_existing = gop.found_existing,4016 .found_existing = gop.found_existing,
4481 .index = @intCast(gop.index),4017 .index = @intCast(gop.index),
4482 .atom = &lp.values.items[gop.index],4018 .ref = &lp.values.items[gop.index],
4483 };4019 };
4484 }4020 }
44854021
...@@ -4525,12 +4061,6 @@ const HotUpdateState = struct {...@@ -4525,12 +4061,6 @@ const HotUpdateState = struct {
4525 mach_task: ?std.c.MachTask = null,4061 mach_task: ?std.c.MachTask = null,
4526};4062};
45274063
4528pub const DynamicRelocs = struct {
4529 rebase_relocs: u32 = 0,
4530 bind_relocs: u32 = 0,
4531 weak_bind_relocs: u32 = 0,
4532};
4533
4534pub const SymtabCtx = struct {4064pub const SymtabCtx = struct {
4535 ilocal: u32 = 0,4065 ilocal: u32 = 0,
4536 istab: u32 = 0,4066 istab: u32 = 0,
...@@ -4540,6 +4070,7 @@ pub const SymtabCtx = struct {...@@ -4540,6 +4070,7 @@ pub const SymtabCtx = struct {
4540 nstabs: u32 = 0,4070 nstabs: u32 = 0,
4541 nexports: u32 = 0,4071 nexports: u32 = 0,
4542 nimports: u32 = 0,4072 nimports: u32 = 0,
4073 stroff: u32 = 0,
4543 strsize: u32 = 0,4074 strsize: u32 = 0,
4544};4075};
45454076
...@@ -4826,6 +4357,136 @@ const UndefinedTreatment = enum {...@@ -4826,6 +4357,136 @@ const UndefinedTreatment = enum {
4826 dynamic_lookup,4357 dynamic_lookup,
4827};4358};
48284359
4360/// A reference to atom or symbol in an input file.
4361/// If file == 0, symbol is an undefined global.
4362pub const Ref = struct {
4363 index: u32,
4364 file: File.Index,
4365
4366 pub fn eql(ref: Ref, other: Ref) bool {
4367 return ref.index == other.index and ref.file == other.file;
4368 }
4369
4370 pub fn getFile(ref: Ref, macho_file: *MachO) ?File {
4371 return macho_file.getFile(ref.file);
4372 }
4373
4374 pub fn getAtom(ref: Ref, macho_file: *MachO) ?*Atom {
4375 const file = ref.getFile(macho_file) orelse return null;
4376 return file.getAtom(ref.index);
4377 }
4378
4379 pub fn getSymbol(ref: Ref, macho_file: *MachO) ?*Symbol {
4380 const file = ref.getFile(macho_file) orelse return null;
4381 return switch (file) {
4382 inline else => |x| &x.symbols.items[ref.index],
4383 };
4384 }
4385
4386 pub fn format(
4387 ref: Ref,
4388 comptime unused_fmt_string: []const u8,
4389 options: std.fmt.FormatOptions,
4390 writer: anytype,
4391 ) !void {
4392 _ = unused_fmt_string;
4393 _ = options;
4394 try writer.print("%{d} in file({d})", .{ ref.index, ref.file });
4395 }
4396};
4397
4398pub const SymbolResolver = struct {
4399 keys: std.ArrayListUnmanaged(Key) = .{},
4400 values: std.ArrayListUnmanaged(Ref) = .{},
4401 table: std.AutoArrayHashMapUnmanaged(void, void) = .{},
4402
4403 const Result = struct {
4404 found_existing: bool,
4405 index: Index,
4406 ref: *Ref,
4407 };
4408
4409 pub fn deinit(resolver: *SymbolResolver, allocator: Allocator) void {
4410 resolver.keys.deinit(allocator);
4411 resolver.values.deinit(allocator);
4412 resolver.table.deinit(allocator);
4413 }
4414
4415 pub fn getOrPut(
4416 resolver: *SymbolResolver,
4417 allocator: Allocator,
4418 ref: Ref,
4419 macho_file: *MachO,
4420 ) !Result {
4421 const adapter = Adapter{ .keys = resolver.keys.items, .macho_file = macho_file };
4422 const key = Key{ .index = ref.index, .file = ref.file };
4423 const gop = try resolver.table.getOrPutAdapted(allocator, key, adapter);
4424 if (!gop.found_existing) {
4425 try resolver.keys.append(allocator, key);
4426 _ = try resolver.values.addOne(allocator);
4427 }
4428 return .{
4429 .found_existing = gop.found_existing,
4430 .index = @intCast(gop.index + 1),
4431 .ref = &resolver.values.items[gop.index],
4432 };
4433 }
4434
4435 pub fn get(resolver: SymbolResolver, index: Index) ?Ref {
4436 if (index == 0) return null;
4437 return resolver.values.items[index - 1];
4438 }
4439
4440 pub fn reset(resolver: *SymbolResolver) void {
4441 resolver.keys.clearRetainingCapacity();
4442 resolver.values.clearRetainingCapacity();
4443 resolver.table.clearRetainingCapacity();
4444 }
4445
4446 const Key = struct {
4447 index: Symbol.Index,
4448 file: File.Index,
4449
4450 fn getName(key: Key, macho_file: *MachO) [:0]const u8 {
4451 const ref = Ref{ .index = key.index, .file = key.file };
4452 return ref.getSymbol(macho_file).?.getName(macho_file);
4453 }
4454
4455 pub fn getFile(key: Key, macho_file: *MachO) ?File {
4456 const ref = Ref{ .index = key.index, .file = key.file };
4457 return ref.getFile(macho_file);
4458 }
4459
4460 fn eql(key: Key, other: Key, macho_file: *MachO) bool {
4461 const key_name = key.getName(macho_file);
4462 const other_name = other.getName(macho_file);
4463 return mem.eql(u8, key_name, other_name);
4464 }
4465
4466 fn hash(key: Key, macho_file: *MachO) u32 {
4467 const name = key.getName(macho_file);
4468 return @truncate(Hash.hash(0, name));
4469 }
4470 };
4471
4472 const Adapter = struct {
4473 keys: []const Key,
4474 macho_file: *MachO,
4475
4476 pub fn eql(ctx: @This(), key: Key, b_void: void, b_map_index: usize) bool {
4477 _ = b_void;
4478 const other = ctx.keys[b_map_index];
4479 return key.eql(other, ctx.macho_file);
4480 }
4481
4482 pub fn hash(ctx: @This(), key: Key) u32 {
4483 return key.hash(ctx.macho_file);
4484 }
4485 };
4486
4487 pub const Index = u32;
4488};
4489
4829const MachO = @This();4490const MachO = @This();
48304491
4831const std = @import("std");4492const std = @import("std");
...@@ -4842,6 +4503,7 @@ const mem = std.mem;...@@ -4842,6 +4503,7 @@ const mem = std.mem;
4842const meta = std.meta;4503const meta = std.meta;
48434504
4844const aarch64 = @import("../arch/aarch64/bits.zig");4505const aarch64 = @import("../arch/aarch64/bits.zig");
4506const bind = @import("MachO/dyld_info/bind.zig");
4845const calcUuid = @import("MachO/uuid.zig").calcUuid;4507const calcUuid = @import("MachO/uuid.zig").calcUuid;
4846const codegen = @import("../codegen.zig");4508const codegen = @import("../codegen.zig");
4847const dead_strip = @import("MachO/dead_strip.zig");4509const dead_strip = @import("MachO/dead_strip.zig");
...@@ -4862,13 +4524,14 @@ const Alignment = Atom.Alignment;...@@ -4862,13 +4524,14 @@ const Alignment = Atom.Alignment;
4862const Allocator = mem.Allocator;4524const Allocator = mem.Allocator;
4863const Archive = @import("MachO/Archive.zig");4525const Archive = @import("MachO/Archive.zig");
4864pub const Atom = @import("MachO/Atom.zig");4526pub const Atom = @import("MachO/Atom.zig");
4865const BindSection = synthetic.BindSection;4527const Bind = bind.Bind;
4866const Cache = std.Build.Cache;4528const Cache = std.Build.Cache;
4867const CodeSignature = @import("MachO/CodeSignature.zig");4529const CodeSignature = @import("MachO/CodeSignature.zig");
4868const Compilation = @import("../Compilation.zig");4530const Compilation = @import("../Compilation.zig");
4531const DataInCode = synthetic.DataInCode;
4869pub const DebugSymbols = @import("MachO/DebugSymbols.zig");4532pub const DebugSymbols = @import("MachO/DebugSymbols.zig");
4870const Dylib = @import("MachO/Dylib.zig");4533const Dylib = @import("MachO/Dylib.zig");
4871const ExportTrieSection = synthetic.ExportTrieSection;4534const ExportTrie = @import("MachO/dyld_info/Trie.zig");
4872const File = @import("MachO/file.zig").File;4535const File = @import("MachO/file.zig").File;
4873const GotSection = synthetic.GotSection;4536const GotSection = synthetic.GotSection;
4874const Hash = std.hash.Wyhash;4537const Hash = std.hash.Wyhash;
...@@ -4876,7 +4539,7 @@ const Indsymtab = synthetic.Indsymtab;...@@ -4876,7 +4539,7 @@ const Indsymtab = synthetic.Indsymtab;
4876const InternalObject = @import("MachO/InternalObject.zig");4539const InternalObject = @import("MachO/InternalObject.zig");
4877const ObjcStubsSection = synthetic.ObjcStubsSection;4540const ObjcStubsSection = synthetic.ObjcStubsSection;
4878const Object = @import("MachO/Object.zig");4541const Object = @import("MachO/Object.zig");
4879const LazyBindSection = synthetic.LazyBindSection;4542const LazyBind = bind.LazyBind;
4880const LaSymbolPtrSection = synthetic.LaSymbolPtrSection;4543const LaSymbolPtrSection = synthetic.LaSymbolPtrSection;
4881const LibStub = tapi.LibStub;4544const LibStub = tapi.LibStub;
4882const Liveness = @import("../Liveness.zig");4545const Liveness = @import("../Liveness.zig");
...@@ -4886,7 +4549,7 @@ const Zcu = @import("../Zcu.zig");...@@ -4886,7 +4549,7 @@ const Zcu = @import("../Zcu.zig");
4886/// Deprecated.4549/// Deprecated.
4887const Module = Zcu;4550const Module = Zcu;
4888const InternPool = @import("../InternPool.zig");4551const InternPool = @import("../InternPool.zig");
4889const RebaseSection = synthetic.RebaseSection;4552const Rebase = @import("MachO/dyld_info/Rebase.zig");
4890pub const Relocation = @import("MachO/Relocation.zig");4553pub const Relocation = @import("MachO/Relocation.zig");
4891const StringTable = @import("StringTable.zig");4554const StringTable = @import("StringTable.zig");
4892const StubsSection = synthetic.StubsSection;4555const StubsSection = synthetic.StubsSection;
...@@ -4896,6 +4559,6 @@ const Thunk = thunks.Thunk;...@@ -4896,6 +4559,6 @@ const Thunk = thunks.Thunk;
4896const TlvPtrSection = synthetic.TlvPtrSection;4559const TlvPtrSection = synthetic.TlvPtrSection;
4897const Value = @import("../Value.zig");4560const Value = @import("../Value.zig");
4898const UnwindInfo = @import("MachO/UnwindInfo.zig");4561const UnwindInfo = @import("MachO/UnwindInfo.zig");
4899const WeakBindSection = synthetic.WeakBindSection;4562const WeakBind = bind.WeakBind;
4900const ZigGotSection = synthetic.ZigGotSection;4563const ZigGotSection = synthetic.ZigGotSection;
4901const ZigObject = @import("MachO/ZigObject.zig");4564const ZigObject = @import("MachO/ZigObject.zig");
src/link/MachO/Archive.zig+2-2
...@@ -67,9 +67,9 @@ pub fn parse(self: *Archive, macho_file: *MachO, path: []const u8, handle_index:...@@ -67,9 +67,9 @@ pub fn parse(self: *Archive, macho_file: *MachO, path: []const u8, handle_index:
67 mem.eql(u8, name, SYMDEF64_SORTED)) continue;67 mem.eql(u8, name, SYMDEF64_SORTED)) continue;
6868
69 const object = Object{69 const object = Object{
70 .archive = .{70 .offset = pos,
71 .in_archive = .{
71 .path = try gpa.dupe(u8, path),72 .path = try gpa.dupe(u8, path),
72 .offset = pos,
73 .size = hdr_size,73 .size = hdr_size,
74 },74 },
75 .path = try gpa.dupe(u8, name),75 .path = try gpa.dupe(u8, name),
src/link/MachO/Atom.zig+116-156
...@@ -47,16 +47,6 @@ pub fn getFile(self: Atom, macho_file: *MachO) File {...@@ -47,16 +47,6 @@ pub fn getFile(self: Atom, macho_file: *MachO) File {
47 return macho_file.getFile(self.file).?;47 return macho_file.getFile(self.file).?;
48}48}
4949
50pub fn getData(self: Atom, macho_file: *MachO, buffer: []u8) !void {
51 assert(buffer.len == self.size);
52 switch (self.getFile(macho_file)) {
53 .internal => |x| try x.getAtomData(self, buffer),
54 .object => |x| try x.getAtomData(macho_file, self, buffer),
55 .zig_object => |x| try x.getAtomData(macho_file, self, buffer),
56 else => unreachable,
57 }
58}
59
60pub fn getRelocs(self: Atom, macho_file: *MachO) []const Relocation {50pub fn getRelocs(self: Atom, macho_file: *MachO) []const Relocation {
61 return switch (self.getFile(macho_file)) {51 return switch (self.getFile(macho_file)) {
62 .dylib => unreachable,52 .dylib => unreachable,
...@@ -88,17 +78,18 @@ pub fn getPriority(self: Atom, macho_file: *MachO) u64 {...@@ -88,17 +78,18 @@ pub fn getPriority(self: Atom, macho_file: *MachO) u64 {
88}78}
8979
90pub fn getUnwindRecords(self: Atom, macho_file: *MachO) []const UnwindInfo.Record.Index {80pub fn getUnwindRecords(self: Atom, macho_file: *MachO) []const UnwindInfo.Record.Index {
91 if (!self.flags.unwind) return &[0]UnwindInfo.Record.Index{};81 const extra = self.getExtra(macho_file);
92 const extra = self.getExtra(macho_file).?;
93 return switch (self.getFile(macho_file)) {82 return switch (self.getFile(macho_file)) {
94 .dylib, .zig_object, .internal => unreachable,83 .dylib => unreachable,
95 .object => |x| x.unwind_records.items[extra.unwind_index..][0..extra.unwind_count],84 .zig_object, .internal => &[0]UnwindInfo.Record.Index{},
85 .object => |x| x.unwind_records_indexes.items[extra.unwind_index..][0..extra.unwind_count],
96 };86 };
97}87}
9888
99pub fn markUnwindRecordsDead(self: Atom, macho_file: *MachO) void {89pub fn markUnwindRecordsDead(self: Atom, macho_file: *MachO) void {
90 const object = self.getFile(macho_file).object;
100 for (self.getUnwindRecords(macho_file)) |cu_index| {91 for (self.getUnwindRecords(macho_file)) |cu_index| {
101 const cu = macho_file.getUnwindRecord(cu_index);92 const cu = object.getUnwindRecord(cu_index);
102 cu.alive = false;93 cu.alive = false;
10394
104 if (cu.getFdePtr(macho_file)) |fde| {95 if (cu.getFdePtr(macho_file)) |fde| {
...@@ -108,44 +99,39 @@ pub fn markUnwindRecordsDead(self: Atom, macho_file: *MachO) void {...@@ -108,44 +99,39 @@ pub fn markUnwindRecordsDead(self: Atom, macho_file: *MachO) void {
108}99}
109100
110pub fn getThunk(self: Atom, macho_file: *MachO) *Thunk {101pub fn getThunk(self: Atom, macho_file: *MachO) *Thunk {
111 assert(self.flags.thunk);102 const extra = self.getExtra(macho_file);
112 const extra = self.getExtra(macho_file).?;
113 return macho_file.getThunk(extra.thunk);103 return macho_file.getThunk(extra.thunk);
114}104}
115105
116pub fn getLiteralPoolIndex(self: Atom, macho_file: *MachO) ?MachO.LiteralPool.Index {
117 if (!self.flags.literal_pool) return null;
118 return self.getExtra(macho_file).?.literal_index;
119}
120
121const AddExtraOpts = struct {106const AddExtraOpts = struct {
122 thunk: ?u32 = null,107 thunk: ?u32 = null,
123 rel_index: ?u32 = null,108 rel_index: ?u32 = null,
124 rel_count: ?u32 = null,109 rel_count: ?u32 = null,
110 rel_out_index: ?u32 = null,
111 rel_out_count: ?u32 = null,
125 unwind_index: ?u32 = null,112 unwind_index: ?u32 = null,
126 unwind_count: ?u32 = null,113 unwind_count: ?u32 = null,
127 literal_index: ?u32 = null,114 literal_pool_index: ?u32 = null,
115 literal_symbol_index: ?u32 = null,
128};116};
129117
130pub fn addExtra(atom: *Atom, opts: AddExtraOpts, macho_file: *MachO) !void {118pub fn addExtra(atom: *Atom, opts: AddExtraOpts, macho_file: *MachO) void {
131 if (atom.getExtra(macho_file) == null) {119 const file = atom.getFile(macho_file);
132 atom.extra = try macho_file.addAtomExtra(.{});120 var extra = file.getAtomExtra(atom.extra);
133 }
134 var extra = atom.getExtra(macho_file).?;
135 inline for (@typeInfo(@TypeOf(opts)).Struct.fields) |field| {121 inline for (@typeInfo(@TypeOf(opts)).Struct.fields) |field| {
136 if (@field(opts, field.name)) |x| {122 if (@field(opts, field.name)) |x| {
137 @field(extra, field.name) = x;123 @field(extra, field.name) = x;
138 }124 }
139 }125 }
140 atom.setExtra(extra, macho_file);126 file.setAtomExtra(atom.extra, extra);
141}127}
142128
143pub inline fn getExtra(atom: Atom, macho_file: *MachO) ?Extra {129pub inline fn getExtra(atom: Atom, macho_file: *MachO) Extra {
144 return macho_file.getAtomExtra(atom.extra);130 return atom.getFile(macho_file).getAtomExtra(atom.extra);
145}131}
146132
147pub inline fn setExtra(atom: Atom, extra: Extra, macho_file: *MachO) void {133pub inline fn setExtra(atom: Atom, extra: Extra, macho_file: *MachO) void {
148 macho_file.setAtomExtra(atom.extra, extra);134 atom.getFile(macho_file).setAtomExtra(atom.extra, extra);
149}135}
150136
151pub fn initOutputSection(sect: macho.section_64, macho_file: *MachO) !u8 {137pub fn initOutputSection(sect: macho.section_64, macho_file: *MachO) !u8 {
...@@ -227,7 +213,8 @@ pub fn initOutputSection(sect: macho.section_64, macho_file: *MachO) !u8 {...@@ -227,7 +213,8 @@ pub fn initOutputSection(sect: macho.section_64, macho_file: *MachO) !u8 {
227/// File offset relocation happens transparently, so it is not included in213/// File offset relocation happens transparently, so it is not included in
228/// this calculation.214/// this calculation.
229pub fn capacity(self: Atom, macho_file: *MachO) u64 {215pub fn capacity(self: Atom, macho_file: *MachO) u64 {
230 const next_addr = if (macho_file.getAtom(self.next_index)) |next|216 const zo = macho_file.getZigObject().?;
217 const next_addr = if (zo.getAtom(self.next_index)) |next|
231 next.getAddress(macho_file)218 next.getAddress(macho_file)
232 else219 else
233 std.math.maxInt(u32);220 std.math.maxInt(u32);
...@@ -236,7 +223,8 @@ pub fn capacity(self: Atom, macho_file: *MachO) u64 {...@@ -236,7 +223,8 @@ pub fn capacity(self: Atom, macho_file: *MachO) u64 {
236223
237pub fn freeListEligible(self: Atom, macho_file: *MachO) bool {224pub fn freeListEligible(self: Atom, macho_file: *MachO) bool {
238 // No need to keep a free list node for the last block.225 // No need to keep a free list node for the last block.
239 const next = macho_file.getAtom(self.next_index) orelse return false;226 const zo = macho_file.getZigObject().?;
227 const next = zo.getAtom(self.next_index) orelse return false;
240 const cap = next.getAddress(macho_file) - self.getAddress(macho_file);228 const cap = next.getAddress(macho_file) - self.getAddress(macho_file);
241 const ideal_cap = MachO.padToIdeal(self.size);229 const ideal_cap = MachO.padToIdeal(self.size);
242 if (cap <= ideal_cap) return false;230 if (cap <= ideal_cap) return false;
...@@ -245,6 +233,7 @@ pub fn freeListEligible(self: Atom, macho_file: *MachO) bool {...@@ -245,6 +233,7 @@ pub fn freeListEligible(self: Atom, macho_file: *MachO) bool {
245}233}
246234
247pub fn allocate(self: *Atom, macho_file: *MachO) !void {235pub fn allocate(self: *Atom, macho_file: *MachO) !void {
236 const zo = macho_file.getZigObject().?;
248 const sect = &macho_file.sections.items(.header)[self.out_n_sect];237 const sect = &macho_file.sections.items(.header)[self.out_n_sect];
249 const free_list = &macho_file.sections.items(.free_list)[self.out_n_sect];238 const free_list = &macho_file.sections.items(.free_list)[self.out_n_sect];
250 const last_atom_index = &macho_file.sections.items(.last_atom_index)[self.out_n_sect];239 const last_atom_index = &macho_file.sections.items(.last_atom_index)[self.out_n_sect];
...@@ -264,7 +253,7 @@ pub fn allocate(self: *Atom, macho_file: *MachO) !void {...@@ -264,7 +253,7 @@ pub fn allocate(self: *Atom, macho_file: *MachO) !void {
264 var i: usize = free_list.items.len;253 var i: usize = free_list.items.len;
265 while (i < free_list.items.len) {254 while (i < free_list.items.len) {
266 const big_atom_index = free_list.items[i];255 const big_atom_index = free_list.items[i];
267 const big_atom = macho_file.getAtom(big_atom_index).?;256 const big_atom = zo.getAtom(big_atom_index).?;
268 // We now have a pointer to a live atom that has too much capacity.257 // We now have a pointer to a live atom that has too much capacity.
269 // Is it enough that we could fit this new atom?258 // Is it enough that we could fit this new atom?
270 const cap = big_atom.capacity(macho_file);259 const cap = big_atom.capacity(macho_file);
...@@ -296,7 +285,7 @@ pub fn allocate(self: *Atom, macho_file: *MachO) !void {...@@ -296,7 +285,7 @@ pub fn allocate(self: *Atom, macho_file: *MachO) !void {
296 free_list_removal = i;285 free_list_removal = i;
297 }286 }
298 break :blk new_start_vaddr;287 break :blk new_start_vaddr;
299 } else if (macho_file.getAtom(last_atom_index.*)) |last| {288 } else if (zo.getAtom(last_atom_index.*)) |last| {
300 const ideal_capacity = MachO.padToIdeal(last.size);289 const ideal_capacity = MachO.padToIdeal(last.size);
301 const ideal_capacity_end_vaddr = last.value + ideal_capacity;290 const ideal_capacity_end_vaddr = last.value + ideal_capacity;
302 const new_start_vaddr = self.alignment.forward(ideal_capacity_end_vaddr);291 const new_start_vaddr = self.alignment.forward(ideal_capacity_end_vaddr);
...@@ -316,7 +305,7 @@ pub fn allocate(self: *Atom, macho_file: *MachO) !void {...@@ -316,7 +305,7 @@ pub fn allocate(self: *Atom, macho_file: *MachO) !void {
316 });305 });
317306
318 const expand_section = if (atom_placement) |placement_index|307 const expand_section = if (atom_placement) |placement_index|
319 macho_file.getAtom(placement_index).?.next_index == 0308 zo.getAtom(placement_index).?.next_index == 0
320 else309 else
321 true;310 true;
322 if (expand_section) {311 if (expand_section) {
...@@ -341,15 +330,15 @@ pub fn allocate(self: *Atom, macho_file: *MachO) !void {...@@ -341,15 +330,15 @@ pub fn allocate(self: *Atom, macho_file: *MachO) !void {
341 // This function can also reallocate an atom.330 // This function can also reallocate an atom.
342 // In this case we need to "unplug" it from its previous location before331 // In this case we need to "unplug" it from its previous location before
343 // plugging it in to its new location.332 // plugging it in to its new location.
344 if (macho_file.getAtom(self.prev_index)) |prev| {333 if (zo.getAtom(self.prev_index)) |prev| {
345 prev.next_index = self.next_index;334 prev.next_index = self.next_index;
346 }335 }
347 if (macho_file.getAtom(self.next_index)) |next| {336 if (zo.getAtom(self.next_index)) |next| {
348 next.prev_index = self.prev_index;337 next.prev_index = self.prev_index;
349 }338 }
350339
351 if (atom_placement) |big_atom_index| {340 if (atom_placement) |big_atom_index| {
352 const big_atom = macho_file.getAtom(big_atom_index).?;341 const big_atom = zo.getAtom(big_atom_index).?;
353 self.prev_index = big_atom_index;342 self.prev_index = big_atom_index;
354 self.next_index = big_atom.next_index;343 self.next_index = big_atom.next_index;
355 big_atom.next_index = self.atom_index;344 big_atom.next_index = self.atom_index;
...@@ -379,6 +368,7 @@ pub fn free(self: *Atom, macho_file: *MachO) void {...@@ -379,6 +368,7 @@ pub fn free(self: *Atom, macho_file: *MachO) void {
379368
380 const comp = macho_file.base.comp;369 const comp = macho_file.base.comp;
381 const gpa = comp.gpa;370 const gpa = comp.gpa;
371 const zo = macho_file.getZigObject().?;
382 const free_list = &macho_file.sections.items(.free_list)[self.out_n_sect];372 const free_list = &macho_file.sections.items(.free_list)[self.out_n_sect];
383 const last_atom_index = &macho_file.sections.items(.last_atom_index)[self.out_n_sect];373 const last_atom_index = &macho_file.sections.items(.last_atom_index)[self.out_n_sect];
384 var already_have_free_list_node = false;374 var already_have_free_list_node = false;
...@@ -397,9 +387,9 @@ pub fn free(self: *Atom, macho_file: *MachO) void {...@@ -397,9 +387,9 @@ pub fn free(self: *Atom, macho_file: *MachO) void {
397 }387 }
398 }388 }
399389
400 if (macho_file.getAtom(last_atom_index.*)) |last_atom| {390 if (zo.getAtom(last_atom_index.*)) |last_atom| {
401 if (last_atom.atom_index == self.atom_index) {391 if (last_atom.atom_index == self.atom_index) {
402 if (macho_file.getAtom(self.prev_index)) |_| {392 if (zo.getAtom(self.prev_index)) |_| {
403 // TODO shrink the section size here393 // TODO shrink the section size here
404 last_atom_index.* = self.prev_index;394 last_atom_index.* = self.prev_index;
405 } else {395 } else {
...@@ -408,7 +398,7 @@ pub fn free(self: *Atom, macho_file: *MachO) void {...@@ -408,7 +398,7 @@ pub fn free(self: *Atom, macho_file: *MachO) void {
408 }398 }
409 }399 }
410400
411 if (macho_file.getAtom(self.prev_index)) |prev| {401 if (zo.getAtom(self.prev_index)) |prev| {
412 prev.next_index = self.next_index;402 prev.next_index = self.next_index;
413 if (!already_have_free_list_node and prev.*.freeListEligible(macho_file)) {403 if (!already_have_free_list_node and prev.*.freeListEligible(macho_file)) {
414 // The free list is heuristics, it doesn't have to be perfect, so we can404 // The free list is heuristics, it doesn't have to be perfect, so we can
...@@ -419,7 +409,7 @@ pub fn free(self: *Atom, macho_file: *MachO) void {...@@ -419,7 +409,7 @@ pub fn free(self: *Atom, macho_file: *MachO) void {
419 self.prev_index = 0;409 self.prev_index = 0;
420 }410 }
421411
422 if (macho_file.getAtom(self.next_index)) |next| {412 if (zo.getAtom(self.next_index)) |next| {
423 next.prev_index = self.prev_index;413 next.prev_index = self.prev_index;
424 } else {414 } else {
425 self.next_index = 0;415 self.next_index = 0;
...@@ -437,8 +427,7 @@ pub fn addReloc(self: *Atom, macho_file: *MachO, reloc: Relocation) !void {...@@ -437,8 +427,7 @@ pub fn addReloc(self: *Atom, macho_file: *MachO, reloc: Relocation) !void {
437 const gpa = macho_file.base.comp.gpa;427 const gpa = macho_file.base.comp.gpa;
438 const file = self.getFile(macho_file);428 const file = self.getFile(macho_file);
439 assert(file == .zig_object);429 assert(file == .zig_object);
440 assert(self.flags.relocs);430 var extra = self.getExtra(macho_file);
441 var extra = self.getExtra(macho_file).?;
442 const rels = &file.zig_object.relocs.items[extra.rel_index];431 const rels = &file.zig_object.relocs.items[extra.rel_index];
443 try rels.append(gpa, reloc);432 try rels.append(gpa, reloc);
444 extra.rel_count += 1;433 extra.rel_count += 1;
...@@ -446,9 +435,8 @@ pub fn addReloc(self: *Atom, macho_file: *MachO, reloc: Relocation) !void {...@@ -446,9 +435,8 @@ pub fn addReloc(self: *Atom, macho_file: *MachO, reloc: Relocation) !void {
446}435}
447436
448pub fn freeRelocs(self: *Atom, macho_file: *MachO) void {437pub fn freeRelocs(self: *Atom, macho_file: *MachO) void {
449 if (!self.flags.relocs) return;
450 self.getFile(macho_file).zig_object.freeAtomRelocs(self.*, macho_file);438 self.getFile(macho_file).zig_object.freeAtomRelocs(self.*, macho_file);
451 var extra = self.getExtra(macho_file).?;439 var extra = self.getExtra(macho_file);
452 extra.rel_count = 0;440 extra.rel_count = 0;
453 self.setExtra(extra, macho_file);441 self.setExtra(extra, macho_file);
454}442}
...@@ -458,11 +446,6 @@ pub fn scanRelocs(self: Atom, macho_file: *MachO) !void {...@@ -458,11 +446,6 @@ pub fn scanRelocs(self: Atom, macho_file: *MachO) !void {
458 defer tracy.end();446 defer tracy.end();
459 assert(self.flags.alive);447 assert(self.flags.alive);
460448
461 const dynrel_ctx = switch (self.getFile(macho_file)) {
462 .zig_object => |x| &x.dynamic_relocs,
463 .object => |x| &x.dynamic_relocs,
464 else => unreachable,
465 };
466 const relocs = self.getRelocs(macho_file);449 const relocs = self.getRelocs(macho_file);
467450
468 for (relocs) |rel| {451 for (relocs) |rel| {
...@@ -470,7 +453,7 @@ pub fn scanRelocs(self: Atom, macho_file: *MachO) !void {...@@ -470,7 +453,7 @@ pub fn scanRelocs(self: Atom, macho_file: *MachO) !void {
470453
471 switch (rel.type) {454 switch (rel.type) {
472 .branch => {455 .branch => {
473 const symbol = rel.getTargetSymbol(macho_file);456 const symbol = rel.getTargetSymbol(self, macho_file);
474 if (symbol.flags.import or (symbol.flags.@"export" and symbol.flags.weak) or symbol.flags.interposable) {457 if (symbol.flags.import or (symbol.flags.@"export" and symbol.flags.weak) or symbol.flags.interposable) {
475 symbol.flags.stubs = true;458 symbol.flags.stubs = true;
476 if (symbol.flags.weak) {459 if (symbol.flags.weak) {
...@@ -485,7 +468,7 @@ pub fn scanRelocs(self: Atom, macho_file: *MachO) !void {...@@ -485,7 +468,7 @@ pub fn scanRelocs(self: Atom, macho_file: *MachO) !void {
485 .got_load_page,468 .got_load_page,
486 .got_load_pageoff,469 .got_load_pageoff,
487 => {470 => {
488 const symbol = rel.getTargetSymbol(macho_file);471 const symbol = rel.getTargetSymbol(self, macho_file);
489 if (symbol.flags.import or472 if (symbol.flags.import or
490 (symbol.flags.@"export" and symbol.flags.weak) or473 (symbol.flags.@"export" and symbol.flags.weak) or
491 symbol.flags.interposable or474 symbol.flags.interposable or
...@@ -499,18 +482,18 @@ pub fn scanRelocs(self: Atom, macho_file: *MachO) !void {...@@ -499,18 +482,18 @@ pub fn scanRelocs(self: Atom, macho_file: *MachO) !void {
499 },482 },
500483
501 .zig_got_load => {484 .zig_got_load => {
502 assert(rel.getTargetSymbol(macho_file).flags.has_zig_got);485 assert(rel.getTargetSymbol(self, macho_file).flags.has_zig_got);
503 },486 },
504487
505 .got => {488 .got => {
506 rel.getTargetSymbol(macho_file).flags.needs_got = true;489 rel.getTargetSymbol(self, macho_file).flags.needs_got = true;
507 },490 },
508491
509 .tlv,492 .tlv,
510 .tlvp_page,493 .tlvp_page,
511 .tlvp_pageoff,494 .tlvp_pageoff,
512 => {495 => {
513 const symbol = rel.getTargetSymbol(macho_file);496 const symbol = rel.getTargetSymbol(self, macho_file);
514 if (!symbol.flags.tlv) {497 if (!symbol.flags.tlv) {
515 try macho_file.reportParseError2(498 try macho_file.reportParseError2(
516 self.getFile(macho_file).getIndex(),499 self.getFile(macho_file).getIndex(),
...@@ -529,27 +512,21 @@ pub fn scanRelocs(self: Atom, macho_file: *MachO) !void {...@@ -529,27 +512,21 @@ pub fn scanRelocs(self: Atom, macho_file: *MachO) !void {
529 .unsigned => {512 .unsigned => {
530 if (rel.meta.length == 3) { // TODO this really should check if this is pointer width513 if (rel.meta.length == 3) { // TODO this really should check if this is pointer width
531 if (rel.tag == .@"extern") {514 if (rel.tag == .@"extern") {
532 const symbol = rel.getTargetSymbol(macho_file);515 const symbol = rel.getTargetSymbol(self, macho_file);
533 if (symbol.isTlvInit(macho_file)) {516 if (symbol.isTlvInit(macho_file)) {
534 macho_file.has_tlv = true;517 macho_file.has_tlv = true;
535 continue;518 continue;
536 }519 }
537 if (symbol.flags.import) {520 if (symbol.flags.import) {
538 dynrel_ctx.bind_relocs += 1;
539 if (symbol.flags.weak) {521 if (symbol.flags.weak) {
540 dynrel_ctx.weak_bind_relocs += 1;
541 macho_file.binds_to_weak = true;522 macho_file.binds_to_weak = true;
542 }523 }
543 continue;524 continue;
544 }525 }
545 if (symbol.flags.@"export" and symbol.flags.weak) {526 if (symbol.flags.@"export" and symbol.flags.weak) {
546 dynrel_ctx.weak_bind_relocs += 1;
547 macho_file.binds_to_weak = true;527 macho_file.binds_to_weak = true;
548 } else if (symbol.flags.interposable) {
549 dynrel_ctx.bind_relocs += 1;
550 }528 }
551 }529 }
552 dynrel_ctx.rebase_relocs += 1;
553 }530 }
554 },531 },
555532
...@@ -568,14 +545,15 @@ pub fn scanRelocs(self: Atom, macho_file: *MachO) !void {...@@ -568,14 +545,15 @@ pub fn scanRelocs(self: Atom, macho_file: *MachO) !void {
568fn reportUndefSymbol(self: Atom, rel: Relocation, macho_file: *MachO) !bool {545fn reportUndefSymbol(self: Atom, rel: Relocation, macho_file: *MachO) !bool {
569 if (rel.tag == .local) return false;546 if (rel.tag == .local) return false;
570547
571 const sym = rel.getTargetSymbol(macho_file);548 const file = self.getFile(macho_file);
572 if (sym.getFile(macho_file) == null) {549 const ref = file.getSymbolRef(rel.target, macho_file);
550 if (ref.getFile(macho_file) == null) {
573 const gpa = macho_file.base.comp.gpa;551 const gpa = macho_file.base.comp.gpa;
574 const gop = try macho_file.undefs.getOrPut(gpa, rel.target);552 const gop = try macho_file.undefs.getOrPut(gpa, file.getGlobals()[rel.target]);
575 if (!gop.found_existing) {553 if (!gop.found_existing) {
576 gop.value_ptr.* = .{};554 gop.value_ptr.* = .{};
577 }555 }
578 try gop.value_ptr.append(gpa, self.atom_index);556 try gop.value_ptr.append(gpa, .{ .index = self.atom_index, .file = self.file });
579 return true;557 return true;
580 }558 }
581559
...@@ -591,7 +569,7 @@ pub fn resolveRelocs(self: Atom, macho_file: *MachO, buffer: []u8) !void {...@@ -591,7 +569,7 @@ pub fn resolveRelocs(self: Atom, macho_file: *MachO, buffer: []u8) !void {
591 const name = self.getName(macho_file);569 const name = self.getName(macho_file);
592 const relocs = self.getRelocs(macho_file);570 const relocs = self.getRelocs(macho_file);
593571
594 relocs_log.debug("{x}: {s}", .{ self.getAddress(macho_file), name });572 relocs_log.debug("{x}: {s}", .{ self.value, name });
595573
596 var has_error = false;574 var has_error = false;
597 var stream = std.io.fixedBufferStream(buffer);575 var stream = std.io.fixedBufferStream(buffer);
...@@ -602,7 +580,7 @@ pub fn resolveRelocs(self: Atom, macho_file: *MachO, buffer: []u8) !void {...@@ -602,7 +580,7 @@ pub fn resolveRelocs(self: Atom, macho_file: *MachO, buffer: []u8) !void {
602 const subtractor = if (rel.meta.has_subtractor) relocs[i - 1] else null;580 const subtractor = if (rel.meta.has_subtractor) relocs[i - 1] else null;
603581
604 if (rel.tag == .@"extern") {582 if (rel.tag == .@"extern") {
605 if (rel.getTargetSymbol(macho_file).getFile(macho_file) == null) continue;583 if (rel.getTargetSymbol(self, macho_file).getFile(macho_file) == null) continue;
606 }584 }
607585
608 try stream.seekTo(rel_offset);586 try stream.seekTo(rel_offset);
...@@ -610,13 +588,19 @@ pub fn resolveRelocs(self: Atom, macho_file: *MachO, buffer: []u8) !void {...@@ -610,13 +588,19 @@ pub fn resolveRelocs(self: Atom, macho_file: *MachO, buffer: []u8) !void {
610 switch (err) {588 switch (err) {
611 error.RelaxFail => {589 error.RelaxFail => {
612 const target = switch (rel.tag) {590 const target = switch (rel.tag) {
613 .@"extern" => rel.getTargetSymbol(macho_file).getName(macho_file),591 .@"extern" => rel.getTargetSymbol(self, macho_file).getName(macho_file),
614 .local => rel.getTargetAtom(macho_file).getName(macho_file),592 .local => rel.getTargetAtom(self, macho_file).getName(macho_file),
615 };593 };
616 try macho_file.reportParseError2(594 try macho_file.reportParseError2(
617 file.getIndex(),595 file.getIndex(),
618 "{s}: 0x{x}: 0x{x}: failed to relax relocation: type {s}, target {s}",596 "{s}: 0x{x}: 0x{x}: failed to relax relocation: type {}, target {s}",
619 .{ name, self.getAddress(macho_file), rel.offset, @tagName(rel.type), target },597 .{
598 name,
599 self.getAddress(macho_file),
600 rel.offset,
601 rel.fmtPretty(macho_file.getTarget().cpu.arch),
602 target,
603 },
620 );604 );
621 has_error = true;605 has_error = true;
622 },606 },
...@@ -649,14 +633,12 @@ fn resolveRelocInner(...@@ -649,14 +633,12 @@ fn resolveRelocInner(
649) ResolveError!void {633) ResolveError!void {
650 const cpu_arch = macho_file.getTarget().cpu.arch;634 const cpu_arch = macho_file.getTarget().cpu.arch;
651 const rel_offset = math.cast(usize, rel.offset - self.off) orelse return error.Overflow;635 const rel_offset = math.cast(usize, rel.offset - self.off) orelse return error.Overflow;
652 const seg_id = macho_file.sections.items(.segment_id)[self.out_n_sect];
653 const seg = macho_file.segments.items[seg_id];
654 const P = @as(i64, @intCast(self.getAddress(macho_file))) + @as(i64, @intCast(rel_offset));636 const P = @as(i64, @intCast(self.getAddress(macho_file))) + @as(i64, @intCast(rel_offset));
655 const A = rel.addend + rel.getRelocAddend(cpu_arch);637 const A = rel.addend + rel.getRelocAddend(cpu_arch);
656 const S: i64 = @intCast(rel.getTargetAddress(macho_file));638 const S: i64 = @intCast(rel.getTargetAddress(self, macho_file));
657 const G: i64 = @intCast(rel.getGotTargetAddress(macho_file));639 const G: i64 = @intCast(rel.getGotTargetAddress(self, macho_file));
658 const TLS = @as(i64, @intCast(macho_file.getTlsAddress()));640 const TLS = @as(i64, @intCast(macho_file.getTlsAddress()));
659 const SUB = if (subtractor) |sub| @as(i64, @intCast(sub.getTargetAddress(macho_file))) else 0;641 const SUB = if (subtractor) |sub| @as(i64, @intCast(sub.getTargetAddress(self, macho_file))) else 0;
660 // Address of the __got_zig table entry if any.642 // Address of the __got_zig table entry if any.
661 const ZIG_GOT = @as(i64, @intCast(rel.getZigGotTargetAddress(macho_file)));643 const ZIG_GOT = @as(i64, @intCast(rel.getZigGotTargetAddress(macho_file)));
662644
...@@ -674,21 +656,21 @@ fn resolveRelocInner(...@@ -674,21 +656,21 @@ fn resolveRelocInner(
674 }.divExact;656 }.divExact;
675657
676 switch (rel.tag) {658 switch (rel.tag) {
677 .local => relocs_log.debug(" {x}<+{d}>: {s}: [=> {x}] atom({d})", .{659 .local => relocs_log.debug(" {x}<+{d}>: {}: [=> {x}] atom({d})", .{
678 P,660 P,
679 rel_offset,661 rel_offset,
680 @tagName(rel.type),662 rel.fmtPretty(cpu_arch),
681 S + A - SUB,663 S + A - SUB,
682 rel.getTargetAtom(macho_file).atom_index,664 rel.getTargetAtom(self, macho_file).atom_index,
683 }),665 }),
684 .@"extern" => relocs_log.debug(" {x}<+{d}>: {s}: [=> {x}] G({x}) ZG({x}) ({s})", .{666 .@"extern" => relocs_log.debug(" {x}<+{d}>: {}: [=> {x}] G({x}) ZG({x}) ({s})", .{
685 P,667 P,
686 rel_offset,668 rel_offset,
687 @tagName(rel.type),669 rel.fmtPretty(cpu_arch),
688 S + A - SUB,670 S + A - SUB,
689 G + A,671 G + A,
690 ZIG_GOT + A,672 ZIG_GOT + A,
691 rel.getTargetSymbol(macho_file).getName(macho_file),673 rel.getTargetSymbol(self, macho_file).getName(macho_file),
692 }),674 }),
693 }675 }
694676
...@@ -699,34 +681,13 @@ fn resolveRelocInner(...@@ -699,34 +681,13 @@ fn resolveRelocInner(
699 assert(!rel.meta.pcrel);681 assert(!rel.meta.pcrel);
700 if (rel.meta.length == 3) {682 if (rel.meta.length == 3) {
701 if (rel.tag == .@"extern") {683 if (rel.tag == .@"extern") {
702 const sym = rel.getTargetSymbol(macho_file);684 const sym = rel.getTargetSymbol(self, macho_file);
703 if (sym.isTlvInit(macho_file)) {685 if (sym.isTlvInit(macho_file)) {
704 try writer.writeInt(u64, @intCast(S - TLS), .little);686 try writer.writeInt(u64, @intCast(S - TLS), .little);
705 return;687 return;
706 }688 }
707 const entry = bind.Entry{689 if (sym.flags.import) return;
708 .target = rel.target,
709 .offset = @as(u64, @intCast(P)) - seg.vmaddr,
710 .segment_id = seg_id,
711 .addend = A,
712 };
713 if (sym.flags.import) {
714 macho_file.bind.entries.appendAssumeCapacity(entry);
715 if (sym.flags.weak) {
716 macho_file.weak_bind.entries.appendAssumeCapacity(entry);
717 }
718 return;
719 }
720 if (sym.flags.@"export" and sym.flags.weak) {
721 macho_file.weak_bind.entries.appendAssumeCapacity(entry);
722 } else if (sym.flags.interposable) {
723 macho_file.bind.entries.appendAssumeCapacity(entry);
724 }
725 }690 }
726 macho_file.rebase.entries.appendAssumeCapacity(.{
727 .offset = @as(u64, @intCast(P)) - seg.vmaddr,
728 .segment_id = seg_id,
729 });
730 try writer.writeInt(u64, @bitCast(S + A - SUB), .little);691 try writer.writeInt(u64, @bitCast(S + A - SUB), .little);
731 } else if (rel.meta.length == 2) {692 } else if (rel.meta.length == 2) {
732 try writer.writeInt(u32, @bitCast(@as(i32, @truncate(S + A - SUB))), .little);693 try writer.writeInt(u32, @bitCast(@as(i32, @truncate(S + A - SUB))), .little);
...@@ -750,7 +711,7 @@ fn resolveRelocInner(...@@ -750,7 +711,7 @@ fn resolveRelocInner(
750 .aarch64 => {711 .aarch64 => {
751 const disp: i28 = math.cast(i28, S + A - P) orelse blk: {712 const disp: i28 = math.cast(i28, S + A - P) orelse blk: {
752 const thunk = self.getThunk(macho_file);713 const thunk = self.getThunk(macho_file);
753 const S_: i64 = @intCast(thunk.getTargetAddress(rel.target, macho_file));714 const S_: i64 = @intCast(thunk.getTargetAddress(rel.getTargetSymbolRef(self, macho_file), macho_file));
754 break :blk math.cast(i28, S_ + A - P) orelse return error.Overflow;715 break :blk math.cast(i28, S_ + A - P) orelse return error.Overflow;
755 };716 };
756 aarch64.writeBranchImm(disp, code[rel_offset..][0..4]);717 aarch64.writeBranchImm(disp, code[rel_offset..][0..4]);
...@@ -763,7 +724,7 @@ fn resolveRelocInner(...@@ -763,7 +724,7 @@ fn resolveRelocInner(
763 assert(rel.tag == .@"extern");724 assert(rel.tag == .@"extern");
764 assert(rel.meta.length == 2);725 assert(rel.meta.length == 2);
765 assert(rel.meta.pcrel);726 assert(rel.meta.pcrel);
766 if (rel.getTargetSymbol(macho_file).flags.has_got) {727 if (rel.getTargetSymbol(self, macho_file).flags.has_got) {
767 try writer.writeInt(i32, @intCast(G + A - P), .little);728 try writer.writeInt(i32, @intCast(G + A - P), .little);
768 } else {729 } else {
769 try x86_64.relaxGotLoad(self, code[rel_offset - 3 ..], rel, macho_file);730 try x86_64.relaxGotLoad(self, code[rel_offset - 3 ..], rel, macho_file);
...@@ -786,7 +747,7 @@ fn resolveRelocInner(...@@ -786,7 +747,7 @@ fn resolveRelocInner(
786 assert(rel.tag == .@"extern");747 assert(rel.tag == .@"extern");
787 assert(rel.meta.length == 2);748 assert(rel.meta.length == 2);
788 assert(rel.meta.pcrel);749 assert(rel.meta.pcrel);
789 const sym = rel.getTargetSymbol(macho_file);750 const sym = rel.getTargetSymbol(self, macho_file);
790 if (sym.flags.tlv_ptr) {751 if (sym.flags.tlv_ptr) {
791 const S_: i64 = @intCast(sym.getTlvPtrAddress(macho_file));752 const S_: i64 = @intCast(sym.getTlvPtrAddress(macho_file));
792 try writer.writeInt(i32, @intCast(S_ + A - P), .little);753 try writer.writeInt(i32, @intCast(S_ + A - P), .little);
...@@ -809,7 +770,7 @@ fn resolveRelocInner(...@@ -809,7 +770,7 @@ fn resolveRelocInner(
809 assert(rel.tag == .@"extern");770 assert(rel.tag == .@"extern");
810 assert(rel.meta.length == 2);771 assert(rel.meta.length == 2);
811 assert(rel.meta.pcrel);772 assert(rel.meta.pcrel);
812 const sym = rel.getTargetSymbol(macho_file);773 const sym = rel.getTargetSymbol(self, macho_file);
813 const source = math.cast(u64, P) orelse return error.Overflow;774 const source = math.cast(u64, P) orelse return error.Overflow;
814 const target = target: {775 const target = target: {
815 const target = switch (rel.type) {776 const target = switch (rel.type) {
...@@ -868,7 +829,7 @@ fn resolveRelocInner(...@@ -868,7 +829,7 @@ fn resolveRelocInner(
868 assert(rel.meta.length == 2);829 assert(rel.meta.length == 2);
869 assert(!rel.meta.pcrel);830 assert(!rel.meta.pcrel);
870831
871 const sym = rel.getTargetSymbol(macho_file);832 const sym = rel.getTargetSymbol(self, macho_file);
872 const target = target: {833 const target = target: {
873 const target = if (sym.flags.tlv_ptr) blk: {834 const target = if (sym.flags.tlv_ptr) blk: {
874 const S_: i64 = @intCast(sym.getTlvPtrAddress(macho_file));835 const S_: i64 = @intCast(sym.getTlvPtrAddress(macho_file));
...@@ -945,11 +906,11 @@ const x86_64 = struct {...@@ -945,11 +906,11 @@ const x86_64 = struct {
945 },906 },
946 else => |x| {907 else => |x| {
947 var err = try macho_file.addErrorWithNotes(2);908 var err = try macho_file.addErrorWithNotes(2);
948 try err.addMsg(macho_file, "{s}: 0x{x}: 0x{x}: failed to relax relocation of type {s}", .{909 try err.addMsg(macho_file, "{s}: 0x{x}: 0x{x}: failed to relax relocation of type {}", .{
949 self.getName(macho_file),910 self.getName(macho_file),
950 self.getAddress(macho_file),911 self.getAddress(macho_file),
951 rel.offset,912 rel.offset,
952 @tagName(rel.type),913 rel.fmtPretty(.x86_64),
953 });914 });
954 try err.addNote(macho_file, "expected .mov instruction but found .{s}", .{@tagName(x)});915 try err.addNote(macho_file, "expected .mov instruction but found .{s}", .{@tagName(x)});
955 try err.addNote(macho_file, "while parsing {}", .{self.getFile(macho_file).fmtPath()});916 try err.addNote(macho_file, "while parsing {}", .{self.getFile(macho_file).fmtPath()});
...@@ -1012,48 +973,49 @@ pub fn calcNumRelocs(self: Atom, macho_file: *MachO) u32 {...@@ -1012,48 +973,49 @@ pub fn calcNumRelocs(self: Atom, macho_file: *MachO) u32 {
1012 }973 }
1013}974}
1014975
1015pub fn writeRelocs(self: Atom, macho_file: *MachO, code: []u8, buffer: *std.ArrayList(macho.relocation_info)) !void {976pub fn writeRelocs(self: Atom, macho_file: *MachO, code: []u8, buffer: []macho.relocation_info) !void {
1016 const tracy = trace(@src());977 const tracy = trace(@src());
1017 defer tracy.end();978 defer tracy.end();
1018979
1019 const cpu_arch = macho_file.getTarget().cpu.arch;980 const cpu_arch = macho_file.getTarget().cpu.arch;
1020 const relocs = self.getRelocs(macho_file);981 const relocs = self.getRelocs(macho_file);
1021 var stream = std.io.fixedBufferStream(code);
1022982
983 var i: usize = 0;
1023 for (relocs) |rel| {984 for (relocs) |rel| {
1024 const rel_offset = rel.offset - self.off;985 defer i += 1;
986 const rel_offset = math.cast(usize, rel.offset - self.off) orelse return error.Overflow;
1025 const r_address: i32 = math.cast(i32, self.value + rel_offset) orelse return error.Overflow;987 const r_address: i32 = math.cast(i32, self.value + rel_offset) orelse return error.Overflow;
988 assert(r_address >= 0);
1026 const r_symbolnum = r_symbolnum: {989 const r_symbolnum = r_symbolnum: {
1027 const r_symbolnum: u32 = switch (rel.tag) {990 const r_symbolnum: u32 = switch (rel.tag) {
1028 .local => rel.getTargetAtom(macho_file).out_n_sect + 1,991 .local => rel.getTargetAtom(self, macho_file).out_n_sect + 1,
1029 .@"extern" => rel.getTargetSymbol(macho_file).getOutputSymtabIndex(macho_file).?,992 .@"extern" => rel.getTargetSymbol(self, macho_file).getOutputSymtabIndex(macho_file).?,
1030 };993 };
1031 break :r_symbolnum math.cast(u24, r_symbolnum) orelse return error.Overflow;994 break :r_symbolnum math.cast(u24, r_symbolnum) orelse return error.Overflow;
1032 };995 };
1033 const r_extern = rel.tag == .@"extern";996 const r_extern = rel.tag == .@"extern";
1034 var addend = rel.addend + rel.getRelocAddend(cpu_arch);997 var addend = rel.addend + rel.getRelocAddend(cpu_arch);
1035 if (rel.tag == .local) {998 if (rel.tag == .local) {
1036 const target: i64 = @intCast(rel.getTargetAddress(macho_file));999 const target: i64 = @intCast(rel.getTargetAddress(self, macho_file));
1037 addend += target;1000 addend += target;
1038 }1001 }
10391002
1040 try stream.seekTo(rel_offset);
1041
1042 switch (cpu_arch) {1003 switch (cpu_arch) {
1043 .aarch64 => {1004 .aarch64 => {
1044 if (rel.type == .unsigned) switch (rel.meta.length) {1005 if (rel.type == .unsigned) switch (rel.meta.length) {
1045 0, 1 => unreachable,1006 0, 1 => unreachable,
1046 2 => try stream.writer().writeInt(i32, @truncate(addend), .little),1007 2 => mem.writeInt(i32, code[rel_offset..][0..4], @truncate(addend), .little),
1047 3 => try stream.writer().writeInt(i64, addend, .little),1008 3 => mem.writeInt(i64, code[rel_offset..][0..8], addend, .little),
1048 } else if (addend > 0) {1009 } else if (addend > 0) {
1049 buffer.appendAssumeCapacity(.{1010 buffer[i] = .{
1050 .r_address = r_address,1011 .r_address = r_address,
1051 .r_symbolnum = @bitCast(math.cast(i24, addend) orelse return error.Overflow),1012 .r_symbolnum = @bitCast(math.cast(i24, addend) orelse return error.Overflow),
1052 .r_pcrel = 0,1013 .r_pcrel = 0,
1053 .r_length = 2,1014 .r_length = 2,
1054 .r_extern = 0,1015 .r_extern = 0,
1055 .r_type = @intFromEnum(macho.reloc_type_arm64.ARM64_RELOC_ADDEND),1016 .r_type = @intFromEnum(macho.reloc_type_arm64.ARM64_RELOC_ADDEND),
1056 });1017 };
1018 i += 1;
1057 }1019 }
10581020
1059 const r_type: macho.reloc_type_arm64 = switch (rel.type) {1021 const r_type: macho.reloc_type_arm64 = switch (rel.type) {
...@@ -1077,14 +1039,14 @@ pub fn writeRelocs(self: Atom, macho_file: *MachO, code: []u8, buffer: *std.Arra...@@ -1077,14 +1039,14 @@ pub fn writeRelocs(self: Atom, macho_file: *MachO, code: []u8, buffer: *std.Arra
1077 .tlv,1039 .tlv,
1078 => unreachable,1040 => unreachable,
1079 };1041 };
1080 buffer.appendAssumeCapacity(.{1042 buffer[i] = .{
1081 .r_address = r_address,1043 .r_address = r_address,
1082 .r_symbolnum = r_symbolnum,1044 .r_symbolnum = r_symbolnum,
1083 .r_pcrel = @intFromBool(rel.meta.pcrel),1045 .r_pcrel = @intFromBool(rel.meta.pcrel),
1084 .r_extern = @intFromBool(r_extern),1046 .r_extern = @intFromBool(r_extern),
1085 .r_length = rel.meta.length,1047 .r_length = rel.meta.length,
1086 .r_type = @intFromEnum(r_type),1048 .r_type = @intFromEnum(r_type),
1087 });1049 };
1088 },1050 },
1089 .x86_64 => {1051 .x86_64 => {
1090 if (rel.meta.pcrel) {1052 if (rel.meta.pcrel) {
...@@ -1096,8 +1058,8 @@ pub fn writeRelocs(self: Atom, macho_file: *MachO, code: []u8, buffer: *std.Arra...@@ -1096,8 +1058,8 @@ pub fn writeRelocs(self: Atom, macho_file: *MachO, code: []u8, buffer: *std.Arra
1096 }1058 }
1097 switch (rel.meta.length) {1059 switch (rel.meta.length) {
1098 0, 1 => unreachable,1060 0, 1 => unreachable,
1099 2 => try stream.writer().writeInt(i32, @truncate(addend), .little),1061 2 => mem.writeInt(i32, code[rel_offset..][0..4], @truncate(addend), .little),
1100 3 => try stream.writer().writeInt(i64, addend, .little),1062 3 => mem.writeInt(i64, code[rel_offset..][0..8], addend, .little),
1101 }1063 }
11021064
1103 const r_type: macho.reloc_type_x86_64 = switch (rel.type) {1065 const r_type: macho.reloc_type_x86_64 = switch (rel.type) {
...@@ -1121,18 +1083,20 @@ pub fn writeRelocs(self: Atom, macho_file: *MachO, code: []u8, buffer: *std.Arra...@@ -1121,18 +1083,20 @@ pub fn writeRelocs(self: Atom, macho_file: *MachO, code: []u8, buffer: *std.Arra
1121 .tlvp_pageoff,1083 .tlvp_pageoff,
1122 => unreachable,1084 => unreachable,
1123 };1085 };
1124 buffer.appendAssumeCapacity(.{1086 buffer[i] = .{
1125 .r_address = r_address,1087 .r_address = r_address,
1126 .r_symbolnum = r_symbolnum,1088 .r_symbolnum = r_symbolnum,
1127 .r_pcrel = @intFromBool(rel.meta.pcrel),1089 .r_pcrel = @intFromBool(rel.meta.pcrel),
1128 .r_extern = @intFromBool(r_extern),1090 .r_extern = @intFromBool(r_extern),
1129 .r_length = rel.meta.length,1091 .r_length = rel.meta.length,
1130 .r_type = @intFromEnum(r_type),1092 .r_type = @intFromEnum(r_type),
1131 });1093 };
1132 },1094 },
1133 else => unreachable,1095 else => unreachable,
1134 }1096 }
1135 }1097 }
1098
1099 assert(i == buffer.len);
1136}1100}
11371101
1138pub fn format(1102pub fn format(
...@@ -1170,18 +1134,18 @@ fn format2(...@@ -1170,18 +1134,18 @@ fn format2(
1170 _ = unused_fmt_string;1134 _ = unused_fmt_string;
1171 const atom = ctx.atom;1135 const atom = ctx.atom;
1172 const macho_file = ctx.macho_file;1136 const macho_file = ctx.macho_file;
1173 try writer.print("atom({d}) : {s} : @{x} : sect({d}) : align({x}) : size({x}) : nreloc({d})", .{1137 const file = atom.getFile(macho_file);
1174 atom.atom_index, atom.getName(macho_file), atom.getAddress(macho_file),1138 try writer.print("atom({d}) : {s} : @{x} : sect({d}) : align({x}) : size({x}) : nreloc({d}) : thunk({d})", .{
1175 atom.out_n_sect, atom.alignment, atom.size,1139 atom.atom_index, atom.getName(macho_file), atom.getAddress(macho_file),
1176 atom.getRelocs(macho_file).len,1140 atom.out_n_sect, atom.alignment, atom.size,
1141 atom.getRelocs(macho_file).len, atom.getExtra(macho_file).thunk,
1177 });1142 });
1178 if (atom.flags.thunk) try writer.print(" : thunk({d})", .{atom.getExtra(macho_file).?.thunk});
1179 if (!atom.flags.alive) try writer.writeAll(" : [*]");1143 if (!atom.flags.alive) try writer.writeAll(" : [*]");
1180 if (atom.flags.unwind) {1144 if (atom.getUnwindRecords(macho_file).len > 0) {
1181 try writer.writeAll(" : unwind{ ");1145 try writer.writeAll(" : unwind{ ");
1182 const extra = atom.getExtra(macho_file).?;1146 const extra = atom.getExtra(macho_file);
1183 for (atom.getUnwindRecords(macho_file), extra.unwind_index..) |index, i| {1147 for (atom.getUnwindRecords(macho_file), extra.unwind_index..) |index, i| {
1184 const rec = macho_file.getUnwindRecord(index);1148 const rec = file.object.getUnwindRecord(index);
1185 try writer.print("{d}", .{index});1149 try writer.print("{d}", .{index});
1186 if (!rec.alive) try writer.writeAll("([*])");1150 if (!rec.alive) try writer.writeAll("([*])");
1187 if (i < extra.unwind_index + extra.unwind_count - 1) try writer.writeAll(", ");1151 if (i < extra.unwind_index + extra.unwind_count - 1) try writer.writeAll(", ");
...@@ -1198,18 +1162,6 @@ pub const Flags = packed struct {...@@ -1198,18 +1162,6 @@ pub const Flags = packed struct {
11981162
1199 /// Specifies if this atom has been visited during garbage collection.1163 /// Specifies if this atom has been visited during garbage collection.
1200 visited: bool = false,1164 visited: bool = false,
1201
1202 /// Whether this atom has a range extension thunk.
1203 thunk: bool = false,
1204
1205 /// Whether this atom has any relocations.
1206 relocs: bool = false,
1207
1208 /// Whether this atom has any unwind records.
1209 unwind: bool = false,
1210
1211 /// Whether this atom has LiteralPool entry.
1212 literal_pool: bool = false,
1213};1165};
12141166
1215pub const Extra = struct {1167pub const Extra = struct {
...@@ -1222,6 +1174,12 @@ pub const Extra = struct {...@@ -1222,6 +1174,12 @@ pub const Extra = struct {
1222 /// Count of relocations belonging to this atom.1174 /// Count of relocations belonging to this atom.
1223 rel_count: u32 = 0,1175 rel_count: u32 = 0,
12241176
1177 /// Start index of relocations being written out to file for this atom.
1178 rel_out_index: u32 = 0,
1179
1180 /// Count of relocations written out to file for this atom.
1181 rel_out_count: u32 = 0,
1182
1225 /// Start index of relocations belonging to this atom.1183 /// Start index of relocations belonging to this atom.
1226 unwind_index: u32 = 0,1184 unwind_index: u32 = 0,
12271185
...@@ -1229,14 +1187,16 @@ pub const Extra = struct {...@@ -1229,14 +1187,16 @@ pub const Extra = struct {
1229 unwind_count: u32 = 0,1187 unwind_count: u32 = 0,
12301188
1231 /// Index into LiteralPool entry for this atom.1189 /// Index into LiteralPool entry for this atom.
1232 literal_index: u32 = 0,1190 literal_pool_index: u32 = 0,
1191
1192 /// Index into the File's symbol table for local symbol representing this literal atom.
1193 literal_symbol_index: u32 = 0,
1233};1194};
12341195
1235pub const Alignment = @import("../../InternPool.zig").Alignment;1196pub const Alignment = @import("../../InternPool.zig").Alignment;
12361197
1237const aarch64 = @import("../aarch64.zig");1198const aarch64 = @import("../aarch64.zig");
1238const assert = std.debug.assert;1199const assert = std.debug.assert;
1239const bind = @import("dyld_info/bind.zig");
1240const macho = std.macho;1200const macho = std.macho;
1241const math = std.math;1201const math = std.math;
1242const mem = std.mem;1202const mem = std.mem;
src/link/MachO/DebugSymbols.zig+6-6
...@@ -175,8 +175,9 @@ fn findFreeSpace(self: *DebugSymbols, object_size: u64, min_alignment: u64) u64...@@ -175,8 +175,9 @@ fn findFreeSpace(self: *DebugSymbols, object_size: u64, min_alignment: u64) u64
175}175}
176176
177pub fn flushModule(self: *DebugSymbols, macho_file: *MachO) !void {177pub fn flushModule(self: *DebugSymbols, macho_file: *MachO) !void {
178 const zo = macho_file.getZigObject().?;
178 for (self.relocs.items) |*reloc| {179 for (self.relocs.items) |*reloc| {
179 const sym = macho_file.getSymbol(reloc.target);180 const sym = zo.symbols.items[reloc.target];
180 const sym_name = sym.getName(macho_file);181 const sym_name = sym.getName(macho_file);
181 const addr = switch (reloc.type) {182 const addr = switch (reloc.type) {
182 .direct_load => sym.getAddress(.{}, macho_file),183 .direct_load => sym.getAddress(.{}, macho_file),
...@@ -382,23 +383,22 @@ pub fn writeSymtab(self: *DebugSymbols, off: u32, macho_file: *MachO) !u32 {...@@ -382,23 +383,22 @@ pub fn writeSymtab(self: *DebugSymbols, off: u32, macho_file: *MachO) !u32 {
382 cmd.symoff = off;383 cmd.symoff = off;
383384
384 try self.symtab.resize(gpa, cmd.nsyms);385 try self.symtab.resize(gpa, cmd.nsyms);
385 try self.strtab.ensureUnusedCapacity(gpa, cmd.strsize - 1);386 try self.strtab.resize(gpa, cmd.strsize);
387 self.strtab.items[0] = 0;
386388
387 if (macho_file.getZigObject()) |zo| {389 if (macho_file.getZigObject()) |zo| {
388 zo.writeSymtab(macho_file, self);390 zo.writeSymtab(macho_file, self);
389 }391 }
390 for (macho_file.objects.items) |index| {392 for (macho_file.objects.items) |index| {
391 try macho_file.getFile(index).?.writeSymtab(macho_file, self);393 macho_file.getFile(index).?.writeSymtab(macho_file, self);
392 }394 }
393 for (macho_file.dylibs.items) |index| {395 for (macho_file.dylibs.items) |index| {
394 try macho_file.getFile(index).?.writeSymtab(macho_file, self);396 macho_file.getFile(index).?.writeSymtab(macho_file, self);
395 }397 }
396 if (macho_file.getInternalObject()) |internal| {398 if (macho_file.getInternalObject()) |internal| {
397 internal.writeSymtab(macho_file, self);399 internal.writeSymtab(macho_file, self);
398 }400 }
399401
400 assert(self.strtab.items.len == cmd.strsize);
401
402 try self.file.pwriteAll(mem.sliceAsBytes(self.symtab.items), cmd.symoff);402 try self.file.pwriteAll(mem.sliceAsBytes(self.symtab.items), cmd.symoff);
403403
404 return off + cmd.nsyms * @sizeOf(macho.nlist_64);404 return off + cmd.nsyms * @sizeOf(macho.nlist_64);
src/link/MachO/Dylib.zig+136-60
...@@ -6,7 +6,9 @@ strtab: std.ArrayListUnmanaged(u8) = .{},...@@ -6,7 +6,9 @@ strtab: std.ArrayListUnmanaged(u8) = .{},
6id: ?Id = null,6id: ?Id = null,
7ordinal: u16 = 0,7ordinal: u16 = 0,
88
9symbols: std.ArrayListUnmanaged(Symbol.Index) = .{},9symbols: std.ArrayListUnmanaged(Symbol) = .{},
10symbols_extra: std.ArrayListUnmanaged(u32) = .{},
11globals: std.ArrayListUnmanaged(MachO.SymbolResolver.Index) = .{},
10dependents: std.ArrayListUnmanaged(Id) = .{},12dependents: std.ArrayListUnmanaged(Id) = .{},
11rpaths: std.StringArrayHashMapUnmanaged(void) = .{},13rpaths: std.StringArrayHashMapUnmanaged(void) = .{},
12umbrella: File.Index = 0,14umbrella: File.Index = 0,
...@@ -37,6 +39,8 @@ pub fn deinit(self: *Dylib, allocator: Allocator) void {...@@ -37,6 +39,8 @@ pub fn deinit(self: *Dylib, allocator: Allocator) void {
37 self.strtab.deinit(allocator);39 self.strtab.deinit(allocator);
38 if (self.id) |*id| id.deinit(allocator);40 if (self.id) |*id| id.deinit(allocator);
39 self.symbols.deinit(allocator);41 self.symbols.deinit(allocator);
42 self.symbols_extra.deinit(allocator);
43 self.globals.deinit(allocator);
40 for (self.dependents.items) |*id| {44 for (self.dependents.items) |*id| {
41 id.deinit(allocator);45 id.deinit(allocator);
42 }46 }
...@@ -494,53 +498,55 @@ fn addObjCExport(...@@ -494,53 +498,55 @@ fn addObjCExport(
494pub fn initSymbols(self: *Dylib, macho_file: *MachO) !void {498pub fn initSymbols(self: *Dylib, macho_file: *MachO) !void {
495 const gpa = macho_file.base.comp.gpa;499 const gpa = macho_file.base.comp.gpa;
496500
497 try self.symbols.ensureTotalCapacityPrecise(gpa, self.exports.items(.name).len);501 const nsyms = self.exports.items(.name).len;
498502 try self.symbols.ensureTotalCapacityPrecise(gpa, nsyms);
499 for (self.exports.items(.name)) |noff| {503 try self.symbols_extra.ensureTotalCapacityPrecise(gpa, nsyms * @sizeOf(Symbol.Extra));
500 const name = self.getString(noff);504 try self.globals.ensureTotalCapacityPrecise(gpa, nsyms);
501 const off = try macho_file.strings.insert(gpa, name);505 self.globals.resize(gpa, nsyms) catch unreachable;
502 const gop = try macho_file.getOrCreateGlobal(off);506 @memset(self.globals.items, 0);
503 self.symbols.addOneAssumeCapacity().* = gop.index;507
508 for (self.exports.items(.name), self.exports.items(.flags)) |noff, flags| {
509 const index = self.addSymbolAssumeCapacity();
510 const symbol = &self.symbols.items[index];
511 symbol.name = noff;
512 symbol.extra = self.addSymbolExtraAssumeCapacity(.{});
513 symbol.flags.weak = flags.weak;
514 symbol.flags.tlv = flags.tlv;
515 symbol.visibility = .global;
504 }516 }
505}517}
506518
507pub fn resolveSymbols(self: *Dylib, macho_file: *MachO) void {519pub fn resolveSymbols(self: *Dylib, macho_file: *MachO) !void {
508 const tracy = trace(@src());520 const tracy = trace(@src());
509 defer tracy.end();521 defer tracy.end();
510522
511 if (!self.explicit and !self.hoisted) return;523 if (!self.explicit and !self.hoisted) return;
512524
513 for (self.symbols.items, self.exports.items(.flags)) |index, flags| {525 const gpa = macho_file.base.comp.gpa;
514 const global = macho_file.getSymbol(index);526
527 for (self.exports.items(.flags), self.globals.items, 0..) |flags, *global, i| {
528 const gop = try macho_file.resolver.getOrPut(gpa, .{
529 .index = @intCast(i),
530 .file = self.index,
531 }, macho_file);
532 if (!gop.found_existing) {
533 gop.ref.* = .{ .index = 0, .file = 0 };
534 }
535 global.* = gop.index;
536
537 if (gop.ref.getFile(macho_file) == null) {
538 gop.ref.* = .{ .index = @intCast(i), .file = self.index };
539 continue;
540 }
541
515 if (self.asFile().getSymbolRank(.{542 if (self.asFile().getSymbolRank(.{
516 .weak = flags.weak,543 .weak = flags.weak,
517 }) < global.getSymbolRank(macho_file)) {544 }) < gop.ref.getSymbol(macho_file).?.getSymbolRank(macho_file)) {
518 global.value = 0;545 gop.ref.* = .{ .index = @intCast(i), .file = self.index };
519 global.atom = 0;
520 global.nlist_idx = 0;
521 global.file = self.index;
522 global.flags.weak = flags.weak;
523 global.flags.tlv = flags.tlv;
524 global.flags.dyn_ref = false;
525 global.flags.tentative = false;
526 global.visibility = .global;
527 }546 }
528 }547 }
529}548}
530549
531pub fn resetGlobals(self: *Dylib, macho_file: *MachO) void {
532 for (self.symbols.items) |sym_index| {
533 const sym = macho_file.getSymbol(sym_index);
534 const name = sym.name;
535 const global = sym.flags.global;
536 const weak_ref = sym.flags.weak_ref;
537 sym.* = .{};
538 sym.name = name;
539 sym.flags.global = global;
540 sym.flags.weak_ref = weak_ref;
541 }
542}
543
544pub fn isAlive(self: Dylib, macho_file: *MachO) bool {550pub fn isAlive(self: Dylib, macho_file: *MachO) bool {
545 if (!macho_file.dead_strip_dylibs) return self.explicit or self.referenced or self.needed;551 if (!macho_file.dead_strip_dylibs) return self.explicit or self.referenced or self.needed;
546 return self.referenced or self.needed;552 return self.referenced or self.needed;
...@@ -550,30 +556,31 @@ pub fn markReferenced(self: *Dylib, macho_file: *MachO) void {...@@ -550,30 +556,31 @@ pub fn markReferenced(self: *Dylib, macho_file: *MachO) void {
550 const tracy = trace(@src());556 const tracy = trace(@src());
551 defer tracy.end();557 defer tracy.end();
552558
553 for (self.symbols.items) |global_index| {559 for (0..self.symbols.items.len) |i| {
554 const global = macho_file.getSymbol(global_index);560 const ref = self.getSymbolRef(@intCast(i), macho_file);
555 const file_ptr = global.getFile(macho_file) orelse continue;561 const file = ref.getFile(macho_file) orelse continue;
556 if (file_ptr.getIndex() != self.index) continue;562 if (file.getIndex() != self.index) continue;
563 const global = ref.getSymbol(macho_file).?;
557 if (global.isLocal()) continue;564 if (global.isLocal()) continue;
558 self.referenced = true;565 self.referenced = true;
559 break;566 break;
560 }567 }
561}568}
562569
563pub fn calcSymtabSize(self: *Dylib, macho_file: *MachO) !void {570pub fn calcSymtabSize(self: *Dylib, macho_file: *MachO) void {
564 const tracy = trace(@src());571 const tracy = trace(@src());
565 defer tracy.end();572 defer tracy.end();
566573
567 for (self.symbols.items) |global_index| {574 for (self.symbols.items, 0..) |*sym, i| {
568 const global = macho_file.getSymbol(global_index);575 const ref = self.getSymbolRef(@intCast(i), macho_file);
569 const file_ptr = global.getFile(macho_file) orelse continue;576 const file = ref.getFile(macho_file) orelse continue;
570 if (file_ptr.getIndex() != self.index) continue;577 if (file.getIndex() != self.index) continue;
571 if (global.isLocal()) continue;578 if (sym.isLocal()) continue;
572 assert(global.flags.import);579 assert(sym.flags.import);
573 global.flags.output_symtab = true;580 sym.flags.output_symtab = true;
574 try global.addExtra(.{ .symtab = self.output_symtab_ctx.nimports }, macho_file);581 sym.addExtra(.{ .symtab = self.output_symtab_ctx.nimports }, macho_file);
575 self.output_symtab_ctx.nimports += 1;582 self.output_symtab_ctx.nimports += 1;
576 self.output_symtab_ctx.strsize += @as(u32, @intCast(global.getName(macho_file).len + 1));583 self.output_symtab_ctx.strsize += @as(u32, @intCast(sym.getName(macho_file).len + 1));
577 }584 }
578}585}
579586
...@@ -581,17 +588,20 @@ pub fn writeSymtab(self: Dylib, macho_file: *MachO, ctx: anytype) void {...@@ -581,17 +588,20 @@ pub fn writeSymtab(self: Dylib, macho_file: *MachO, ctx: anytype) void {
581 const tracy = trace(@src());588 const tracy = trace(@src());
582 defer tracy.end();589 defer tracy.end();
583590
584 for (self.symbols.items) |global_index| {591 var n_strx = self.output_symtab_ctx.stroff;
585 const global = macho_file.getSymbol(global_index);592 for (self.symbols.items, 0..) |sym, i| {
586 const file = global.getFile(macho_file) orelse continue;593 const ref = self.getSymbolRef(@intCast(i), macho_file);
594 const file = ref.getFile(macho_file) orelse continue;
587 if (file.getIndex() != self.index) continue;595 if (file.getIndex() != self.index) continue;
588 const idx = global.getOutputSymtabIndex(macho_file) orelse continue;596 const idx = sym.getOutputSymtabIndex(macho_file) orelse continue;
589 const n_strx = @as(u32, @intCast(ctx.strtab.items.len));
590 ctx.strtab.appendSliceAssumeCapacity(global.getName(macho_file));
591 ctx.strtab.appendAssumeCapacity(0);
592 const out_sym = &ctx.symtab.items[idx];597 const out_sym = &ctx.symtab.items[idx];
593 out_sym.n_strx = n_strx;598 out_sym.n_strx = n_strx;
594 global.setOutputSym(macho_file, out_sym);599 sym.setOutputSym(macho_file, out_sym);
600 const name = sym.getName(macho_file);
601 @memcpy(ctx.strtab.items[n_strx..][0..name.len], name);
602 n_strx += @intCast(name.len);
603 ctx.strtab.items[n_strx] = 0;
604 n_strx += 1;
595 }605 }
596}606}
597607
...@@ -605,7 +615,7 @@ fn addString(self: *Dylib, allocator: Allocator, name: []const u8) !u32 {...@@ -605,7 +615,7 @@ fn addString(self: *Dylib, allocator: Allocator, name: []const u8) !u32 {
605 return off;615 return off;
606}616}
607617
608pub inline fn getString(self: Dylib, off: u32) [:0]const u8 {618pub fn getString(self: Dylib, off: u32) [:0]const u8 {
609 assert(off < self.strtab.items.len);619 assert(off < self.strtab.items.len);
610 return mem.sliceTo(@as([*:0]const u8, @ptrCast(self.strtab.items.ptr + off)), 0);620 return mem.sliceTo(@as([*:0]const u8, @ptrCast(self.strtab.items.ptr + off)), 0);
611}621}
...@@ -614,6 +624,66 @@ pub fn asFile(self: *Dylib) File {...@@ -614,6 +624,66 @@ pub fn asFile(self: *Dylib) File {
614 return .{ .dylib = self };624 return .{ .dylib = self };
615}625}
616626
627fn addSymbol(self: *Dylib, allocator: Allocator) !Symbol.Index {
628 try self.symbols.ensureUnusedCapacity(allocator, 1);
629 return self.addSymbolAssumeCapacity();
630}
631
632fn addSymbolAssumeCapacity(self: *Dylib) Symbol.Index {
633 const index: Symbol.Index = @intCast(self.symbols.items.len);
634 const symbol = self.symbols.addOneAssumeCapacity();
635 symbol.* = .{ .file = self.index };
636 return index;
637}
638
639pub fn getSymbolRef(self: Dylib, index: Symbol.Index, macho_file: *MachO) MachO.Ref {
640 const global_index = self.globals.items[index];
641 if (macho_file.resolver.get(global_index)) |ref| return ref;
642 return .{ .index = index, .file = self.index };
643}
644
645pub fn addSymbolExtra(self: *Dylib, allocator: Allocator, extra: Symbol.Extra) !u32 {
646 const fields = @typeInfo(Symbol.Extra).Struct.fields;
647 try self.symbols_extra.ensureUnusedCapacity(allocator, fields.len);
648 return self.addSymbolExtraAssumeCapacity(extra);
649}
650
651fn addSymbolExtraAssumeCapacity(self: *Dylib, extra: Symbol.Extra) u32 {
652 const index = @as(u32, @intCast(self.symbols_extra.items.len));
653 const fields = @typeInfo(Symbol.Extra).Struct.fields;
654 inline for (fields) |field| {
655 self.symbols_extra.appendAssumeCapacity(switch (field.type) {
656 u32 => @field(extra, field.name),
657 else => @compileError("bad field type"),
658 });
659 }
660 return index;
661}
662
663pub fn getSymbolExtra(self: Dylib, index: u32) Symbol.Extra {
664 const fields = @typeInfo(Symbol.Extra).Struct.fields;
665 var i: usize = index;
666 var result: Symbol.Extra = undefined;
667 inline for (fields) |field| {
668 @field(result, field.name) = switch (field.type) {
669 u32 => self.symbols_extra.items[i],
670 else => @compileError("bad field type"),
671 };
672 i += 1;
673 }
674 return result;
675}
676
677pub fn setSymbolExtra(self: *Dylib, index: u32, extra: Symbol.Extra) void {
678 const fields = @typeInfo(Symbol.Extra).Struct.fields;
679 inline for (fields, 0..) |field, i| {
680 self.symbols_extra.items[index + i] = switch (field.type) {
681 u32 => @field(extra, field.name),
682 else => @compileError("bad field type"),
683 };
684 }
685}
686
617pub fn format(687pub fn format(
618 self: *Dylib,688 self: *Dylib,
619 comptime unused_fmt_string: []const u8,689 comptime unused_fmt_string: []const u8,
...@@ -648,10 +718,16 @@ fn formatSymtab(...@@ -648,10 +718,16 @@ fn formatSymtab(
648 _ = unused_fmt_string;718 _ = unused_fmt_string;
649 _ = options;719 _ = options;
650 const dylib = ctx.dylib;720 const dylib = ctx.dylib;
721 const macho_file = ctx.macho_file;
651 try writer.writeAll(" globals\n");722 try writer.writeAll(" globals\n");
652 for (dylib.symbols.items) |index| {723 for (dylib.symbols.items, 0..) |sym, i| {
653 const global = ctx.macho_file.getSymbol(index);724 const ref = dylib.getSymbolRef(@intCast(i), macho_file);
654 try writer.print(" {}\n", .{global.fmt(ctx.macho_file)});725 if (ref.getFile(macho_file) == null) {
726 // TODO any better way of handling this?
727 try writer.print(" {s} : unclaimed\n", .{sym.getName(macho_file)});
728 } else {
729 try writer.print(" {}\n", .{ref.getSymbol(macho_file).?.fmt(macho_file)});
730 }
655 }731 }
656}732}
657733
src/link/MachO/InternalObject.zig+652-143
...@@ -1,13 +1,28 @@...@@ -1,13 +1,28 @@
1index: File.Index,1index: File.Index,
22
3sections: std.MultiArrayList(Section) = .{},3sections: std.MultiArrayList(Section) = .{},
4atoms: std.ArrayListUnmanaged(Atom.Index) = .{},4atoms: std.ArrayListUnmanaged(Atom) = .{},
5symbols: std.ArrayListUnmanaged(Symbol.Index) = .{},5atoms_indexes: std.ArrayListUnmanaged(Atom.Index) = .{},
6atoms_extra: std.ArrayListUnmanaged(u32) = .{},
7symtab: std.ArrayListUnmanaged(macho.nlist_64) = .{},
8strtab: std.ArrayListUnmanaged(u8) = .{},
9symbols: std.ArrayListUnmanaged(Symbol) = .{},
10symbols_extra: std.ArrayListUnmanaged(u32) = .{},
11globals: std.ArrayListUnmanaged(MachO.SymbolResolver.Index) = .{},
612
7objc_methnames: std.ArrayListUnmanaged(u8) = .{},13objc_methnames: std.ArrayListUnmanaged(u8) = .{},
8objc_selrefs: [@sizeOf(u64)]u8 = [_]u8{0} ** @sizeOf(u64),14objc_selrefs: [@sizeOf(u64)]u8 = [_]u8{0} ** @sizeOf(u64),
915
10num_rebase_relocs: u32 = 0,16force_undefined: std.ArrayListUnmanaged(Symbol.Index) = .{},
17entry_index: ?Symbol.Index = null,
18dyld_stub_binder_index: ?Symbol.Index = null,
19dyld_private_index: ?Symbol.Index = null,
20objc_msg_send_index: ?Symbol.Index = null,
21mh_execute_header_index: ?Symbol.Index = null,
22mh_dylib_header_index: ?Symbol.Index = null,
23dso_handle_index: ?Symbol.Index = null,
24boundary_symbols: std.ArrayListUnmanaged(Symbol.Index) = .{},
25
11output_symtab_ctx: MachO.SymtabCtx = .{},26output_symtab_ctx: MachO.SymtabCtx = .{},
1227
13pub fn deinit(self: *InternalObject, allocator: Allocator) void {28pub fn deinit(self: *InternalObject, allocator: Allocator) void {
...@@ -16,39 +31,224 @@ pub fn deinit(self: *InternalObject, allocator: Allocator) void {...@@ -16,39 +31,224 @@ pub fn deinit(self: *InternalObject, allocator: Allocator) void {
16 }31 }
17 self.sections.deinit(allocator);32 self.sections.deinit(allocator);
18 self.atoms.deinit(allocator);33 self.atoms.deinit(allocator);
34 self.atoms_indexes.deinit(allocator);
35 self.atoms_extra.deinit(allocator);
36 self.symtab.deinit(allocator);
37 self.strtab.deinit(allocator);
19 self.symbols.deinit(allocator);38 self.symbols.deinit(allocator);
39 self.symbols_extra.deinit(allocator);
40 self.globals.deinit(allocator);
20 self.objc_methnames.deinit(allocator);41 self.objc_methnames.deinit(allocator);
42 self.force_undefined.deinit(allocator);
43 self.boundary_symbols.deinit(allocator);
44}
45
46pub fn init(self: *InternalObject, allocator: Allocator) !void {
47 // Atom at index 0 is reserved as null atom.
48 try self.atoms.append(allocator, .{});
49 try self.atoms_extra.append(allocator, 0);
50 // Null byte in strtab
51 try self.strtab.append(allocator, 0);
21}52}
2253
23pub fn addSymbol(self: *InternalObject, name: [:0]const u8, macho_file: *MachO) !Symbol.Index {54pub fn initSymbols(self: *InternalObject, macho_file: *MachO) !void {
55 const newSymbolAssumeCapacity = struct {
56 fn newSymbolAssumeCapacity(obj: *InternalObject, name: u32, args: struct {
57 type: u8 = macho.N_UNDF | macho.N_EXT,
58 desc: u16 = 0,
59 }) Symbol.Index {
60 const index = obj.addSymbolAssumeCapacity();
61 const symbol = &obj.symbols.items[index];
62 symbol.name = name;
63 symbol.extra = obj.addSymbolExtraAssumeCapacity(.{});
64 symbol.flags.dyn_ref = args.desc & macho.REFERENCED_DYNAMICALLY != 0;
65 symbol.visibility = if (args.type & macho.N_EXT != 0) blk: {
66 break :blk if (args.type & macho.N_PEXT != 0) .hidden else .global;
67 } else .local;
68
69 const nlist_idx: u32 = @intCast(obj.symtab.items.len);
70 const nlist = obj.symtab.addOneAssumeCapacity();
71 nlist.* = .{
72 .n_strx = name,
73 .n_type = args.type,
74 .n_sect = 0,
75 .n_desc = args.desc,
76 .n_value = 0,
77 };
78 symbol.nlist_idx = nlist_idx;
79 return index;
80 }
81 }.newSymbolAssumeCapacity;
82
24 const gpa = macho_file.base.comp.gpa;83 const gpa = macho_file.base.comp.gpa;
25 try self.symbols.ensureUnusedCapacity(gpa, 1);84 var nsyms = macho_file.base.comp.force_undefined_symbols.keys().len;
26 const off = try macho_file.strings.insert(gpa, name);85 nsyms += 1; // dyld_stub_binder
27 const gop = try macho_file.getOrCreateGlobal(off);86 nsyms += 1; // _objc_msgSend
28 self.symbols.addOneAssumeCapacity().* = gop.index;87 if (!macho_file.base.isDynLib()) {
29 const sym = macho_file.getSymbol(gop.index);88 nsyms += 1; // entry
30 sym.file = self.index;89 nsyms += 1; // __mh_execute_header
31 sym.value = 0;90 } else {
32 sym.atom = 0;91 nsyms += 1; // __mh_dylib_header
33 sym.nlist_idx = 0;92 }
34 sym.flags = .{ .global = true };93 nsyms += 1; // ___dso_handle
35 return gop.index;94 nsyms += 1; // dyld_private
95
96 try self.symbols.ensureTotalCapacityPrecise(gpa, nsyms);
97 try self.symbols_extra.ensureTotalCapacityPrecise(gpa, nsyms * @sizeOf(Symbol.Extra));
98 try self.symtab.ensureTotalCapacityPrecise(gpa, nsyms);
99 try self.globals.ensureTotalCapacityPrecise(gpa, nsyms);
100 self.globals.resize(gpa, nsyms) catch unreachable;
101 @memset(self.globals.items, 0);
102
103 try self.force_undefined.ensureTotalCapacityPrecise(gpa, macho_file.base.comp.force_undefined_symbols.keys().len);
104 for (macho_file.base.comp.force_undefined_symbols.keys()) |name| {
105 self.force_undefined.addOneAssumeCapacity().* = newSymbolAssumeCapacity(self, try self.addString(gpa, name), .{});
106 }
107
108 self.dyld_stub_binder_index = newSymbolAssumeCapacity(self, try self.addString(gpa, "dyld_stub_binder"), .{});
109 self.objc_msg_send_index = newSymbolAssumeCapacity(self, try self.addString(gpa, "_objc_msgSend"), .{});
110
111 if (!macho_file.base.isDynLib()) {
112 self.entry_index = newSymbolAssumeCapacity(self, try self.addString(gpa, macho_file.entry_name orelse "_main"), .{});
113 self.mh_execute_header_index = newSymbolAssumeCapacity(self, try self.addString(gpa, "__mh_execute_header"), .{
114 .type = macho.N_SECT | macho.N_EXT,
115 .desc = macho.REFERENCED_DYNAMICALLY,
116 });
117 } else {
118 self.mh_dylib_header_index = newSymbolAssumeCapacity(self, try self.addString(gpa, "__mh_dylib_header"), .{
119 .type = macho.N_SECT | macho.N_EXT,
120 });
121 }
122
123 self.dso_handle_index = newSymbolAssumeCapacity(self, try self.addString(gpa, "___dso_handle"), .{
124 .type = macho.N_SECT | macho.N_EXT,
125 });
126 self.dyld_private_index = newSymbolAssumeCapacity(self, try self.addString(gpa, "dyld_private"), .{
127 .type = macho.N_SECT,
128 });
36}129}
37130
38/// Creates a fake input sections __TEXT,__objc_methname and __DATA,__objc_selrefs.131pub fn resolveSymbols(self: *InternalObject, macho_file: *MachO) !void {
39pub fn addObjcMsgsendSections(self: *InternalObject, sym_name: []const u8, macho_file: *MachO) !Atom.Index {132 const tracy = trace(@src());
40 const methname_atom_index = try self.addObjcMethnameSection(sym_name, macho_file);133 defer tracy.end();
41 return try self.addObjcSelrefsSection(methname_atom_index, macho_file);134
135 const gpa = macho_file.base.comp.gpa;
136
137 for (self.symtab.items, self.globals.items, 0..) |nlist, *global, i| {
138 const gop = try macho_file.resolver.getOrPut(gpa, .{
139 .index = @intCast(i),
140 .file = self.index,
141 }, macho_file);
142 if (!gop.found_existing) {
143 gop.ref.* = .{ .index = 0, .file = 0 };
144 }
145 global.* = gop.index;
146
147 if (nlist.undf()) continue;
148 if (gop.ref.getFile(macho_file) == null) {
149 gop.ref.* = .{ .index = @intCast(i), .file = self.index };
150 continue;
151 }
152
153 if (self.asFile().getSymbolRank(.{
154 .archive = false,
155 .weak = false,
156 .tentative = false,
157 }) < gop.ref.getSymbol(macho_file).?.getSymbolRank(macho_file)) {
158 gop.ref.* = .{ .index = @intCast(i), .file = self.index };
159 }
160 }
42}161}
43162
44fn addObjcMethnameSection(self: *InternalObject, methname: []const u8, macho_file: *MachO) !Atom.Index {163pub fn resolveBoundarySymbols(self: *InternalObject, macho_file: *MachO) !void {
164 const tracy = trace(@src());
165 defer tracy.end();
166
45 const gpa = macho_file.base.comp.gpa;167 const gpa = macho_file.base.comp.gpa;
46 const atom_index = try macho_file.addAtom();168 var boundary_symbols = std.StringArrayHashMap(MachO.Ref).init(gpa);
47 try self.atoms.append(gpa, atom_index);169 defer boundary_symbols.deinit();
170
171 for (macho_file.objects.items) |index| {
172 const object = macho_file.getFile(index).?.object;
173 for (object.symbols.items, 0..) |sym, i| {
174 const nlist = object.symtab.items(.nlist)[i];
175 if (!nlist.undf() or !nlist.ext()) continue;
176 const ref = object.getSymbolRef(@intCast(i), macho_file);
177 if (ref.getFile(macho_file) != null) continue;
178 const name = sym.getName(macho_file);
179 if (mem.startsWith(u8, name, "segment$start$") or
180 mem.startsWith(u8, name, "segment$stop$") or
181 mem.startsWith(u8, name, "section$start$") or
182 mem.startsWith(u8, name, "section$stop$"))
183 {
184 const gop = try boundary_symbols.getOrPut(name);
185 if (!gop.found_existing) {
186 gop.value_ptr.* = .{ .index = @intCast(i), .file = index };
187 }
188 }
189 }
190 }
191
192 const nsyms = boundary_symbols.values().len;
193 try self.boundary_symbols.ensureTotalCapacityPrecise(gpa, nsyms);
194 try self.symbols.ensureUnusedCapacity(gpa, nsyms);
195 try self.symtab.ensureUnusedCapacity(gpa, nsyms);
196 try self.symbols_extra.ensureUnusedCapacity(gpa, nsyms * @sizeOf(Symbol.Extra));
197 try self.globals.ensureUnusedCapacity(gpa, nsyms);
198
199 for (boundary_symbols.keys(), boundary_symbols.values()) |name, ref| {
200 const name_off = try self.addString(gpa, name);
201 const sym_index = self.addSymbolAssumeCapacity();
202 self.boundary_symbols.appendAssumeCapacity(sym_index);
203 const sym = &self.symbols.items[sym_index];
204 sym.name = name_off;
205 sym.visibility = .local;
206 const nlist_idx: u32 = @intCast(self.symtab.items.len);
207 const nlist = self.symtab.addOneAssumeCapacity();
208 nlist.* = .{
209 .n_strx = name_off,
210 .n_type = macho.N_SECT,
211 .n_sect = 0,
212 .n_desc = 0,
213 .n_value = 0,
214 };
215 sym.nlist_idx = nlist_idx;
216 sym.extra = self.addSymbolExtraAssumeCapacity(.{});
217
218 const idx = ref.getFile(macho_file).?.object.globals.items[ref.index];
219 self.globals.addOneAssumeCapacity().* = idx;
220 macho_file.resolver.values.items[idx - 1] = .{ .index = sym_index, .file = self.index };
221 }
222}
223
224pub fn markLive(self: *InternalObject, macho_file: *MachO) void {
225 const tracy = trace(@src());
226 defer tracy.end();
227
228 for (0..self.symbols.items.len) |i| {
229 const nlist = self.symtab.items[i];
230 if (!nlist.ext()) continue;
231
232 const ref = self.getSymbolRef(@intCast(i), macho_file);
233 const file = ref.getFile(macho_file) orelse continue;
234 if (file == .object and !file.object.alive) {
235 file.object.alive = true;
236 file.object.markLive(macho_file);
237 }
238 }
239}
48240
49 const atom = macho_file.getAtom(atom_index).?;241/// Creates a fake input sections __TEXT,__objc_methname and __DATA,__objc_selrefs.
50 atom.atom_index = atom_index;242pub fn addObjcMsgsendSections(self: *InternalObject, sym_name: []const u8, macho_file: *MachO) !Symbol.Index {
51 atom.file = self.index;243 const methname_sym_index = try self.addObjcMethnameSection(sym_name, macho_file);
244 return try self.addObjcSelrefsSection(methname_sym_index, macho_file);
245}
246
247fn addObjcMethnameSection(self: *InternalObject, methname: []const u8, macho_file: *MachO) !Symbol.Index {
248 const gpa = macho_file.base.comp.gpa;
249 const atom_index = try self.addAtom(gpa);
250 try self.atoms_indexes.append(gpa, atom_index);
251 const atom = self.getAtom(atom_index).?;
52 atom.size = methname.len + 1;252 atom.size = methname.len + 1;
53 atom.alignment = .@"1";253 atom.alignment = .@"1";
54254
...@@ -64,17 +264,32 @@ fn addObjcMethnameSection(self: *InternalObject, methname: []const u8, macho_fil...@@ -64,17 +264,32 @@ fn addObjcMethnameSection(self: *InternalObject, methname: []const u8, macho_fil
64 try self.objc_methnames.ensureUnusedCapacity(gpa, methname.len + 1);264 try self.objc_methnames.ensureUnusedCapacity(gpa, methname.len + 1);
65 self.objc_methnames.writer(gpa).print("{s}\x00", .{methname}) catch unreachable;265 self.objc_methnames.writer(gpa).print("{s}\x00", .{methname}) catch unreachable;
66266
267 const name_str = try self.addString(gpa, "ltmp");
268 const sym_index = try self.addSymbol(gpa);
269 const sym = &self.symbols.items[sym_index];
270 sym.name = name_str;
271 sym.atom_ref = .{ .index = atom_index, .file = self.index };
272 sym.extra = try self.addSymbolExtra(gpa, .{});
273 const nlist_idx: u32 = @intCast(self.symtab.items.len);
274 const nlist = try self.symtab.addOne(gpa);
275 nlist.* = .{
276 .n_strx = name_str,
277 .n_type = macho.N_SECT,
278 .n_sect = @intCast(n_sect + 1),
279 .n_desc = 0,
280 .n_value = 0,
281 };
282 sym.nlist_idx = nlist_idx;
283 try self.globals.append(gpa, 0);
284
67 return atom_index;285 return atom_index;
68}286}
69287
70fn addObjcSelrefsSection(self: *InternalObject, methname_atom_index: Atom.Index, macho_file: *MachO) !Atom.Index {288fn addObjcSelrefsSection(self: *InternalObject, methname_sym_index: Symbol.Index, macho_file: *MachO) !Symbol.Index {
71 const gpa = macho_file.base.comp.gpa;289 const gpa = macho_file.base.comp.gpa;
72 const atom_index = try macho_file.addAtom();290 const atom_index = try self.addAtom(gpa);
73 try self.atoms.append(gpa, atom_index);291 try self.atoms_indexes.append(gpa, atom_index);
74292 const atom = self.getAtom(atom_index).?;
75 const atom = macho_file.getAtom(atom_index).?;
76 atom.atom_index = atom_index;
77 atom.file = self.index;
78 atom.size = @sizeOf(u64);293 atom.size = @sizeOf(u64);
79 atom.alignment = .@"8";294 atom.alignment = .@"8";
80295
...@@ -90,9 +305,9 @@ fn addObjcSelrefsSection(self: *InternalObject, methname_atom_index: Atom.Index,...@@ -90,9 +305,9 @@ fn addObjcSelrefsSection(self: *InternalObject, methname_atom_index: Atom.Index,
90 const relocs = &self.sections.items(.relocs)[n_sect];305 const relocs = &self.sections.items(.relocs)[n_sect];
91 try relocs.ensureUnusedCapacity(gpa, 1);306 try relocs.ensureUnusedCapacity(gpa, 1);
92 relocs.appendAssumeCapacity(.{307 relocs.appendAssumeCapacity(.{
93 .tag = .local,308 .tag = .@"extern",
94 .offset = 0,309 .offset = 0,
95 .target = methname_atom_index,310 .target = methname_sym_index,
96 .addend = 0,311 .addend = 0,
97 .type = .unsigned,312 .type = .unsigned,
98 .meta = .{313 .meta = .{
...@@ -102,140 +317,285 @@ fn addObjcSelrefsSection(self: *InternalObject, methname_atom_index: Atom.Index,...@@ -102,140 +317,285 @@ fn addObjcSelrefsSection(self: *InternalObject, methname_atom_index: Atom.Index,
102 .has_subtractor = false,317 .has_subtractor = false,
103 },318 },
104 });319 });
105 try atom.addExtra(.{ .rel_index = 0, .rel_count = 1 }, macho_file);320 atom.addExtra(.{ .rel_index = 0, .rel_count = 1 }, macho_file);
106 atom.flags.relocs = true;321
107 self.num_rebase_relocs += 1;322 const sym_index = try self.addSymbol(gpa);
323 const sym = &self.symbols.items[sym_index];
324 sym.atom_ref = .{ .index = atom_index, .file = self.index };
325 sym.extra = try self.addSymbolExtra(gpa, .{});
326 const nlist_idx: u32 = @intCast(self.symtab.items.len);
327 const nlist = try self.symtab.addOne(gpa);
328 nlist.* = .{
329 .n_strx = 0,
330 .n_type = macho.N_SECT,
331 .n_sect = @intCast(n_sect + 1),
332 .n_desc = 0,
333 .n_value = 0,
334 };
335 sym.nlist_idx = nlist_idx;
336 try self.globals.append(gpa, 0);
337 atom.addExtra(.{ .literal_symbol_index = sym_index }, macho_file);
108338
109 return atom_index;339 return sym_index;
110}340}
111341
112pub fn resolveLiterals(self: InternalObject, lp: *MachO.LiteralPool, macho_file: *MachO) !void {342pub fn resolveObjcMsgSendSymbols(self: *InternalObject, macho_file: *MachO) !void {
343 const tracy = trace(@src());
344 defer tracy.end();
345
346 const gpa = macho_file.base.comp.gpa;
347
348 var objc_msgsend_syms = std.StringArrayHashMap(MachO.Ref).init(gpa);
349 defer objc_msgsend_syms.deinit();
350
351 for (macho_file.objects.items) |index| {
352 const object = macho_file.getFile(index).?.object;
353
354 for (object.symbols.items, 0..) |sym, i| {
355 const nlist = object.symtab.items(.nlist)[i];
356 if (!nlist.ext()) continue;
357 if (!nlist.undf()) continue;
358
359 const ref = object.getSymbolRef(@intCast(i), macho_file);
360 if (ref.getFile(macho_file) != null) continue;
361
362 const name = sym.getName(macho_file);
363 if (mem.startsWith(u8, name, "_objc_msgSend$")) {
364 const gop = try objc_msgsend_syms.getOrPut(name);
365 if (!gop.found_existing) {
366 gop.value_ptr.* = .{ .index = @intCast(i), .file = index };
367 }
368 }
369 }
370 }
371
372 for (objc_msgsend_syms.keys(), objc_msgsend_syms.values()) |sym_name, ref| {
373 const name = MachO.eatPrefix(sym_name, "_objc_msgSend$").?;
374 const selrefs_index = try self.addObjcMsgsendSections(name, macho_file);
375
376 const name_off = try self.addString(gpa, sym_name);
377 const sym_index = try self.addSymbol(gpa);
378 const sym = &self.symbols.items[sym_index];
379 sym.name = name_off;
380 sym.visibility = .hidden;
381 const nlist_idx: u32 = @intCast(self.symtab.items.len);
382 const nlist = try self.symtab.addOne(gpa);
383 nlist.* = .{
384 .n_strx = name_off,
385 .n_type = macho.N_SECT | macho.N_EXT | macho.N_PEXT,
386 .n_sect = 0,
387 .n_desc = 0,
388 .n_value = 0,
389 };
390 sym.nlist_idx = nlist_idx;
391 sym.extra = try self.addSymbolExtra(gpa, .{ .objc_selrefs = selrefs_index });
392 sym.flags.objc_stubs = true;
393
394 const idx = ref.getFile(macho_file).?.object.globals.items[ref.index];
395 try self.globals.append(gpa, idx);
396 macho_file.resolver.values.items[idx - 1] = .{ .index = sym_index, .file = self.index };
397 }
398}
399
400pub fn resolveLiterals(self: *InternalObject, lp: *MachO.LiteralPool, macho_file: *MachO) !void {
401 const tracy = trace(@src());
402 defer tracy.end();
403
113 const gpa = macho_file.base.comp.gpa;404 const gpa = macho_file.base.comp.gpa;
114405
115 var buffer = std.ArrayList(u8).init(gpa);406 var buffer = std.ArrayList(u8).init(gpa);
116 defer buffer.deinit();407 defer buffer.deinit();
117408
118 const slice = self.sections.slice();409 const slice = self.sections.slice();
119 for (slice.items(.header), self.atoms.items, 0..) |header, atom_index, n_sect| {410 for (slice.items(.header), self.getAtoms()) |header, atom_index| {
120 if (Object.isCstringLiteral(header) or Object.isFixedSizeLiteral(header)) {411 if (!Object.isPtrLiteral(header)) continue;
121 const data = try self.getSectionData(@intCast(n_sect));412 const atom = self.getAtom(atom_index).?;
122 const atom = macho_file.getAtom(atom_index).?;413 const relocs = atom.getRelocs(macho_file);
123 const res = try lp.insert(gpa, header.type(), data);414 assert(relocs.len == 1);
124 if (!res.found_existing) {415 const rel = relocs[0];
125 res.atom.* = atom_index;416 assert(rel.tag == .@"extern");
126 }417 const target = rel.getTargetSymbol(atom.*, macho_file).getAtom(macho_file).?;
127 atom.flags.literal_pool = true;418 const target_size = std.math.cast(usize, target.size) orelse return error.Overflow;
128 try atom.addExtra(.{ .literal_index = res.index }, macho_file);419 try buffer.ensureUnusedCapacity(target_size);
129 } else if (Object.isPtrLiteral(header)) {420 buffer.resize(target_size) catch unreachable;
130 const atom = macho_file.getAtom(atom_index).?;421 @memcpy(buffer.items, try self.getSectionData(target.n_sect));
131 const relocs = atom.getRelocs(macho_file);422 const res = try lp.insert(gpa, header.type(), buffer.items);
132 assert(relocs.len == 1);423 buffer.clearRetainingCapacity();
133 const rel = relocs[0];424 if (!res.found_existing) {
134 assert(rel.tag == .local);425 res.ref.* = .{ .index = atom.getExtra(macho_file).literal_symbol_index, .file = self.index };
135 const target = macho_file.getAtom(rel.target).?;426 } else {
136 const addend = std.math.cast(u32, rel.addend) orelse return error.Overflow;427 const lp_sym = lp.getSymbol(res.index, macho_file);
137 const target_size = std.math.cast(usize, target.size) orelse return error.Overflow;428 const lp_atom = lp_sym.getAtom(macho_file).?;
138 try buffer.ensureUnusedCapacity(target_size);429 lp_atom.alignment = lp_atom.alignment.max(atom.alignment);
139 buffer.resize(target_size) catch unreachable;430 atom.flags.alive = false;
140 try target.getData(macho_file, buffer.items);
141 const res = try lp.insert(gpa, header.type(), buffer.items[addend..]);
142 buffer.clearRetainingCapacity();
143 if (!res.found_existing) {
144 res.atom.* = atom_index;
145 }
146 atom.flags.literal_pool = true;
147 try atom.addExtra(.{ .literal_index = res.index }, macho_file);
148 }431 }
432 atom.addExtra(.{ .literal_pool_index = res.index }, macho_file);
149 }433 }
150}434}
151435
152pub fn dedupLiterals(self: InternalObject, lp: MachO.LiteralPool, macho_file: *MachO) void {436pub fn dedupLiterals(self: *InternalObject, lp: MachO.LiteralPool, macho_file: *MachO) void {
153 for (self.atoms.items) |atom_index| {437 const tracy = trace(@src());
154 const atom = macho_file.getAtom(atom_index) orelse continue;438 defer tracy.end();
439
440 for (self.getAtoms()) |atom_index| {
441 const atom = self.getAtom(atom_index) orelse continue;
155 if (!atom.flags.alive) continue;442 if (!atom.flags.alive) continue;
156 if (!atom.flags.relocs) continue;
157443
158 const relocs = blk: {444 const relocs = blk: {
159 const extra = atom.getExtra(macho_file).?;445 const extra = atom.getExtra(macho_file);
160 const relocs = self.sections.items(.relocs)[atom.n_sect].items;446 const relocs = self.sections.items(.relocs)[atom.n_sect].items;
161 break :blk relocs[extra.rel_index..][0..extra.rel_count];447 break :blk relocs[extra.rel_index..][0..extra.rel_count];
162 };448 };
163 for (relocs) |*rel| switch (rel.tag) {449 for (relocs) |*rel| {
164 .local => {450 if (rel.tag != .@"extern") continue;
165 const target = macho_file.getAtom(rel.target).?;451 const target_sym_ref = rel.getTargetSymbolRef(atom.*, macho_file);
166 if (target.getLiteralPoolIndex(macho_file)) |lp_index| {452 const file = target_sym_ref.getFile(macho_file) orelse continue;
167 const lp_atom = lp.getAtom(lp_index, macho_file);453 if (file.getIndex() != self.index) continue;
168 if (target.atom_index != lp_atom.atom_index) {454 const target_sym = target_sym_ref.getSymbol(macho_file).?;
169 lp_atom.alignment = lp_atom.alignment.max(target.alignment);455 const target_atom = target_sym.getAtom(macho_file) orelse continue;
170 target.flags.alive = false;456 if (!Object.isPtrLiteral(target_atom.getInputSection(macho_file))) continue;
171 rel.target = lp_atom.atom_index;457 const lp_index = target_atom.getExtra(macho_file).literal_pool_index;
172 }458 const lp_sym = lp.getSymbol(lp_index, macho_file);
173 }459 const lp_atom_ref = lp_sym.atom_ref;
174 },460 if (target_atom.atom_index != lp_atom_ref.index or target_atom.file != lp_atom_ref.file) {
175 .@"extern" => {461 target_sym.atom_ref = lp_atom_ref;
176 const target_sym = rel.getTargetSymbol(macho_file);462 }
177 if (target_sym.getAtom(macho_file)) |target_atom| {463 }
178 if (target_atom.getLiteralPoolIndex(macho_file)) |lp_index| {
179 const lp_atom = lp.getAtom(lp_index, macho_file);
180 if (target_atom.atom_index != lp_atom.atom_index) {
181 lp_atom.alignment = lp_atom.alignment.max(target_atom.alignment);
182 target_atom.flags.alive = false;
183 target_sym.atom = lp_atom.atom_index;
184 }
185 }
186 }
187 },
188 };
189 }464 }
190465
191 for (self.symbols.items) |sym_index| {466 for (self.symbols.items) |*sym| {
192 const sym = macho_file.getSymbol(sym_index);
193 if (!sym.flags.objc_stubs) continue;467 if (!sym.flags.objc_stubs) continue;
194 var extra = sym.getExtra(macho_file).?;468 const extra = sym.getExtra(macho_file);
195 const atom = macho_file.getAtom(extra.objc_selrefs).?;469 const file = sym.getFile(macho_file).?;
196 if (atom.getLiteralPoolIndex(macho_file)) |lp_index| {470 if (file.getIndex() != self.index) continue;
197 const lp_atom = lp.getAtom(lp_index, macho_file);471 const tsym = switch (file) {
198 if (atom.atom_index != lp_atom.atom_index) {472 .dylib => unreachable,
199 lp_atom.alignment = lp_atom.alignment.max(atom.alignment);473 inline else => |x| &x.symbols.items[extra.objc_selrefs],
200 atom.flags.alive = false;474 };
201 extra.objc_selrefs = lp_atom.atom_index;475 const atom = tsym.getAtom(macho_file) orelse continue;
202 sym.setExtra(extra, macho_file);476 if (!Object.isPtrLiteral(atom.getInputSection(macho_file))) continue;
477 const lp_index = atom.getExtra(macho_file).literal_pool_index;
478 const lp_sym = lp.getSymbol(lp_index, macho_file);
479 const lp_atom_ref = lp_sym.atom_ref;
480 if (atom.atom_index != lp_atom_ref.index or atom.file != lp_atom_ref.file) {
481 tsym.atom_ref = lp_atom_ref;
482 }
483 }
484}
485
486pub fn scanRelocs(self: *InternalObject, macho_file: *MachO) void {
487 const tracy = trace(@src());
488 defer tracy.end();
489
490 if (self.getEntryRef(macho_file)) |ref| {
491 if (ref.getFile(macho_file) != null) {
492 const sym = ref.getSymbol(macho_file).?;
493 if (sym.flags.import) sym.flags.stubs = true;
494 }
495 }
496 if (self.getDyldStubBinderRef(macho_file)) |ref| {
497 if (ref.getFile(macho_file) != null) {
498 const sym = ref.getSymbol(macho_file).?;
499 sym.flags.needs_got = true;
500 }
501 }
502 if (self.getObjcMsgSendRef(macho_file)) |ref| {
503 if (ref.getFile(macho_file) != null) {
504 const sym = ref.getSymbol(macho_file).?;
505 // TODO is it always needed, or only if we are synthesising fast stubs
506 sym.flags.needs_got = true;
507 }
508 }
509}
510
511pub fn allocateSyntheticSymbols(self: *InternalObject, macho_file: *MachO) void {
512 const text_seg = macho_file.getTextSegment();
513
514 if (self.mh_execute_header_index) |index| {
515 const ref = self.getSymbolRef(index, macho_file);
516 if (ref.getFile(macho_file)) |file| {
517 if (file.getIndex() == self.index) {
518 const sym = &self.symbols.items[index];
519 sym.value = text_seg.vmaddr;
520 }
521 }
522 }
523
524 if (macho_file.data_sect_index) |idx| {
525 const sect = macho_file.sections.items(.header)[idx];
526 for (&[_]?Symbol.Index{
527 self.dso_handle_index,
528 self.mh_dylib_header_index,
529 self.dyld_private_index,
530 }) |maybe_index| {
531 if (maybe_index) |index| {
532 const ref = self.getSymbolRef(index, macho_file);
533 if (ref.getFile(macho_file)) |file| {
534 if (file.getIndex() == self.index) {
535 const sym = &self.symbols.items[index];
536 sym.value = sect.addr;
537 sym.out_n_sect = idx;
538 }
539 }
203 }540 }
204 }541 }
205 }542 }
206}543}
207544
208pub fn calcSymtabSize(self: *InternalObject, macho_file: *MachO) !void {545pub fn calcSymtabSize(self: *InternalObject, macho_file: *MachO) void {
209 for (self.symbols.items) |sym_index| {546 for (self.symbols.items, 0..) |*sym, i| {
210 const sym = macho_file.getSymbol(sym_index);547 const ref = self.getSymbolRef(@intCast(i), macho_file);
211 if (sym.getFile(macho_file)) |file| if (file.getIndex() != self.index) continue;548 const file = ref.getFile(macho_file) orelse continue;
549 if (file.getIndex() != self.index) continue;
550 if (sym.getName(macho_file).len == 0) continue;
212 sym.flags.output_symtab = true;551 sym.flags.output_symtab = true;
213 if (sym.isLocal()) {552 if (sym.isLocal()) {
214 try sym.addExtra(.{ .symtab = self.output_symtab_ctx.nlocals }, macho_file);553 sym.addExtra(.{ .symtab = self.output_symtab_ctx.nlocals }, macho_file);
215 self.output_symtab_ctx.nlocals += 1;554 self.output_symtab_ctx.nlocals += 1;
216 } else if (sym.flags.@"export") {555 } else if (sym.flags.@"export") {
217 try sym.addExtra(.{ .symtab = self.output_symtab_ctx.nexports }, macho_file);556 sym.addExtra(.{ .symtab = self.output_symtab_ctx.nexports }, macho_file);
218 self.output_symtab_ctx.nexports += 1;557 self.output_symtab_ctx.nexports += 1;
219 } else {558 } else {
220 assert(sym.flags.import);559 assert(sym.flags.import);
221 try sym.addExtra(.{ .symtab = self.output_symtab_ctx.nimports }, macho_file);560 sym.addExtra(.{ .symtab = self.output_symtab_ctx.nimports }, macho_file);
222 self.output_symtab_ctx.nimports += 1;561 self.output_symtab_ctx.nimports += 1;
223 }562 }
224 self.output_symtab_ctx.strsize += @as(u32, @intCast(sym.getName(macho_file).len + 1));563 self.output_symtab_ctx.strsize += @as(u32, @intCast(sym.getName(macho_file).len + 1));
225 }564 }
226}565}
227566
567pub fn writeAtoms(self: *InternalObject, macho_file: *MachO) !void {
568 const tracy = trace(@src());
569 defer tracy.end();
570
571 for (self.getAtoms()) |atom_index| {
572 const atom = self.getAtom(atom_index) orelse continue;
573 if (!atom.flags.alive) continue;
574 const sect = atom.getInputSection(macho_file);
575 if (sect.isZerofill()) continue;
576 const off = std.math.cast(usize, atom.value) orelse return error.Overflow;
577 const size = std.math.cast(usize, atom.size) orelse return error.Overflow;
578 const buffer = macho_file.sections.items(.out)[atom.out_n_sect].items[off..][0..size];
579 @memcpy(buffer, try self.getSectionData(atom.n_sect));
580 try atom.resolveRelocs(macho_file, buffer);
581 }
582}
583
228pub fn writeSymtab(self: InternalObject, macho_file: *MachO, ctx: anytype) void {584pub fn writeSymtab(self: InternalObject, macho_file: *MachO, ctx: anytype) void {
229 for (self.symbols.items) |sym_index| {585 var n_strx = self.output_symtab_ctx.stroff;
230 const sym = macho_file.getSymbol(sym_index);586 for (self.symbols.items, 0..) |sym, i| {
231 if (sym.getFile(macho_file)) |file| if (file.getIndex() != self.index) continue;587 const ref = self.getSymbolRef(@intCast(i), macho_file);
588 const file = ref.getFile(macho_file) orelse continue;
589 if (file.getIndex() != self.index) continue;
232 const idx = sym.getOutputSymtabIndex(macho_file) orelse continue;590 const idx = sym.getOutputSymtabIndex(macho_file) orelse continue;
233 const n_strx = @as(u32, @intCast(ctx.strtab.items.len));
234 ctx.strtab.appendSliceAssumeCapacity(sym.getName(macho_file));
235 ctx.strtab.appendAssumeCapacity(0);
236 const out_sym = &ctx.symtab.items[idx];591 const out_sym = &ctx.symtab.items[idx];
237 out_sym.n_strx = n_strx;592 out_sym.n_strx = n_strx;
238 sym.setOutputSym(macho_file, out_sym);593 sym.setOutputSym(macho_file, out_sym);
594 const name = sym.getName(macho_file);
595 @memcpy(ctx.strtab.items[n_strx..][0..name.len], name);
596 n_strx += @intCast(name.len);
597 ctx.strtab.items[n_strx] = 0;
598 n_strx += 1;
239 }599 }
240}600}
241601
...@@ -264,30 +624,171 @@ fn getSectionData(self: *const InternalObject, index: u32) error{Overflow}![]con...@@ -264,30 +624,171 @@ fn getSectionData(self: *const InternalObject, index: u32) error{Overflow}![]con
264 @panic("ref to non-existent section");624 @panic("ref to non-existent section");
265}625}
266626
267pub fn getAtomData(self: *const InternalObject, atom: Atom, buffer: []u8) error{Overflow}!void {627pub fn addString(self: *InternalObject, allocator: Allocator, name: []const u8) !u32 {
268 assert(buffer.len == atom.size);628 const off: u32 = @intCast(self.strtab.items.len);
269 const data = try self.getSectionData(atom.n_sect);629 try self.strtab.ensureUnusedCapacity(allocator, name.len + 1);
270 const off = std.math.cast(usize, atom.off) orelse return error.Overflow;630 self.strtab.appendSliceAssumeCapacity(name);
271 const size = std.math.cast(usize, atom.size) orelse return error.Overflow;631 self.strtab.appendAssumeCapacity(0);
272 @memcpy(buffer, data[off..][0..size]);632 return off;
633}
634
635pub fn getString(self: InternalObject, off: u32) [:0]const u8 {
636 assert(off < self.strtab.items.len);
637 return mem.sliceTo(@as([*:0]const u8, @ptrCast(self.strtab.items.ptr + off)), 0);
638}
639
640pub fn asFile(self: *InternalObject) File {
641 return .{ .internal = self };
273}642}
274643
275pub fn getAtomRelocs(self: *const InternalObject, atom: Atom, macho_file: *MachO) []const Relocation {644pub fn getAtomRelocs(self: *const InternalObject, atom: Atom, macho_file: *MachO) []const Relocation {
276 if (!atom.flags.relocs) return &[0]Relocation{};645 const extra = atom.getExtra(macho_file);
277 const extra = atom.getExtra(macho_file).?;
278 const relocs = self.sections.items(.relocs)[atom.n_sect];646 const relocs = self.sections.items(.relocs)[atom.n_sect];
279 return relocs.items[extra.rel_index..][0..extra.rel_count];647 return relocs.items[extra.rel_index..][0..extra.rel_count];
280}648}
281649
282pub fn getString(self: InternalObject, off: u32) [:0]const u8 {650fn addAtom(self: *InternalObject, allocator: Allocator) !Atom.Index {
283 _ = self;651 const atom_index: Atom.Index = @intCast(self.atoms.items.len);
284 _ = off;652 const atom = try self.atoms.addOne(allocator);
285 // We don't have any local strings for synthetic atoms.653 atom.* = .{
286 return "";654 .file = self.index,
655 .atom_index = atom_index,
656 .extra = try self.addAtomExtra(allocator, .{}),
657 };
658 return atom_index;
287}659}
288660
289pub fn asFile(self: *InternalObject) File {661pub fn getAtom(self: *InternalObject, atom_index: Atom.Index) ?*Atom {
290 return .{ .internal = self };662 if (atom_index == 0) return null;
663 assert(atom_index < self.atoms.items.len);
664 return &self.atoms.items[atom_index];
665}
666
667pub fn getAtoms(self: InternalObject) []const Atom.Index {
668 return self.atoms_indexes.items;
669}
670
671fn addAtomExtra(self: *InternalObject, allocator: Allocator, extra: Atom.Extra) !u32 {
672 const fields = @typeInfo(Atom.Extra).Struct.fields;
673 try self.atoms_extra.ensureUnusedCapacity(allocator, fields.len);
674 return self.addAtomExtraAssumeCapacity(extra);
675}
676
677fn addAtomExtraAssumeCapacity(self: *InternalObject, extra: Atom.Extra) u32 {
678 const index = @as(u32, @intCast(self.atoms_extra.items.len));
679 const fields = @typeInfo(Atom.Extra).Struct.fields;
680 inline for (fields) |field| {
681 self.atoms_extra.appendAssumeCapacity(switch (field.type) {
682 u32 => @field(extra, field.name),
683 else => @compileError("bad field type"),
684 });
685 }
686 return index;
687}
688
689pub fn getAtomExtra(self: InternalObject, index: u32) Atom.Extra {
690 const fields = @typeInfo(Atom.Extra).Struct.fields;
691 var i: usize = index;
692 var result: Atom.Extra = undefined;
693 inline for (fields) |field| {
694 @field(result, field.name) = switch (field.type) {
695 u32 => self.atoms_extra.items[i],
696 else => @compileError("bad field type"),
697 };
698 i += 1;
699 }
700 return result;
701}
702
703pub fn setAtomExtra(self: *InternalObject, index: u32, extra: Atom.Extra) void {
704 assert(index > 0);
705 const fields = @typeInfo(Atom.Extra).Struct.fields;
706 inline for (fields, 0..) |field, i| {
707 self.atoms_extra.items[index + i] = switch (field.type) {
708 u32 => @field(extra, field.name),
709 else => @compileError("bad field type"),
710 };
711 }
712}
713
714pub fn getEntryRef(self: InternalObject, macho_file: *MachO) ?MachO.Ref {
715 const index = self.entry_index orelse return null;
716 return self.getSymbolRef(index, macho_file);
717}
718
719pub fn getDyldStubBinderRef(self: InternalObject, macho_file: *MachO) ?MachO.Ref {
720 const index = self.dyld_stub_binder_index orelse return null;
721 return self.getSymbolRef(index, macho_file);
722}
723
724pub fn getDyldPrivateRef(self: InternalObject, macho_file: *MachO) ?MachO.Ref {
725 const index = self.dyld_private_index orelse return null;
726 return self.getSymbolRef(index, macho_file);
727}
728
729pub fn getObjcMsgSendRef(self: InternalObject, macho_file: *MachO) ?MachO.Ref {
730 const index = self.objc_msg_send_index orelse return null;
731 return self.getSymbolRef(index, macho_file);
732}
733
734pub fn addSymbol(self: *InternalObject, allocator: Allocator) !Symbol.Index {
735 try self.symbols.ensureUnusedCapacity(allocator, 1);
736 return self.addSymbolAssumeCapacity();
737}
738
739pub fn addSymbolAssumeCapacity(self: *InternalObject) Symbol.Index {
740 const index: Symbol.Index = @intCast(self.symbols.items.len);
741 const symbol = self.symbols.addOneAssumeCapacity();
742 symbol.* = .{ .file = self.index };
743 return index;
744}
745
746pub fn getSymbolRef(self: InternalObject, index: Symbol.Index, macho_file: *MachO) MachO.Ref {
747 const global_index = self.globals.items[index];
748 if (macho_file.resolver.get(global_index)) |ref| return ref;
749 return .{ .index = index, .file = self.index };
750}
751
752pub fn addSymbolExtra(self: *InternalObject, allocator: Allocator, extra: Symbol.Extra) !u32 {
753 const fields = @typeInfo(Symbol.Extra).Struct.fields;
754 try self.symbols_extra.ensureUnusedCapacity(allocator, fields.len);
755 return self.addSymbolExtraAssumeCapacity(extra);
756}
757
758fn addSymbolExtraAssumeCapacity(self: *InternalObject, extra: Symbol.Extra) u32 {
759 const index = @as(u32, @intCast(self.symbols_extra.items.len));
760 const fields = @typeInfo(Symbol.Extra).Struct.fields;
761 inline for (fields) |field| {
762 self.symbols_extra.appendAssumeCapacity(switch (field.type) {
763 u32 => @field(extra, field.name),
764 else => @compileError("bad field type"),
765 });
766 }
767 return index;
768}
769
770pub fn getSymbolExtra(self: InternalObject, index: u32) Symbol.Extra {
771 const fields = @typeInfo(Symbol.Extra).Struct.fields;
772 var i: usize = index;
773 var result: Symbol.Extra = undefined;
774 inline for (fields) |field| {
775 @field(result, field.name) = switch (field.type) {
776 u32 => self.symbols_extra.items[i],
777 else => @compileError("bad field type"),
778 };
779 i += 1;
780 }
781 return result;
782}
783
784pub fn setSymbolExtra(self: *InternalObject, index: u32, extra: Symbol.Extra) void {
785 const fields = @typeInfo(Symbol.Extra).Struct.fields;
786 inline for (fields, 0..) |field, i| {
787 self.symbols_extra.items[index + i] = switch (field.type) {
788 u32 => @field(extra, field.name),
789 else => @compileError("bad field type"),
790 };
791 }
291}792}
292793
293const FormatContext = struct {794const FormatContext = struct {
...@@ -311,8 +812,8 @@ fn formatAtoms(...@@ -311,8 +812,8 @@ fn formatAtoms(
311 _ = unused_fmt_string;812 _ = unused_fmt_string;
312 _ = options;813 _ = options;
313 try writer.writeAll(" atoms\n");814 try writer.writeAll(" atoms\n");
314 for (ctx.self.atoms.items) |atom_index| {815 for (ctx.self.getAtoms()) |atom_index| {
315 const atom = ctx.macho_file.getAtom(atom_index).?;816 const atom = ctx.self.getAtom(atom_index) orelse continue;
316 try writer.print(" {}\n", .{atom.fmt(ctx.macho_file)});817 try writer.print(" {}\n", .{atom.fmt(ctx.macho_file)});
317 }818 }
318}819}
...@@ -332,10 +833,17 @@ fn formatSymtab(...@@ -332,10 +833,17 @@ fn formatSymtab(
332) !void {833) !void {
333 _ = unused_fmt_string;834 _ = unused_fmt_string;
334 _ = options;835 _ = options;
836 const macho_file = ctx.macho_file;
837 const self = ctx.self;
335 try writer.writeAll(" symbols\n");838 try writer.writeAll(" symbols\n");
336 for (ctx.self.symbols.items) |index| {839 for (self.symbols.items, 0..) |sym, i| {
337 const global = ctx.macho_file.getSymbol(index);840 const ref = self.getSymbolRef(@intCast(i), macho_file);
338 try writer.print(" {}\n", .{global.fmt(ctx.macho_file)});841 if (ref.getFile(macho_file) == null) {
842 // TODO any better way of handling this?
843 try writer.print(" {s} : unclaimed\n", .{sym.getName(macho_file)});
844 } else {
845 try writer.print(" {}\n", .{ref.getSymbol(macho_file).?.fmt(macho_file)});
846 }
339 }847 }
340}848}
341849
...@@ -354,6 +862,7 @@ const assert = std.debug.assert;...@@ -354,6 +862,7 @@ const assert = std.debug.assert;
354const macho = std.macho;862const macho = std.macho;
355const mem = std.mem;863const mem = std.mem;
356const std = @import("std");864const std = @import("std");
865const trace = @import("../../tracy.zig").trace;
357866
358const Allocator = std.mem.Allocator;867const Allocator = std.mem.Allocator;
359const Atom = @import("Atom.zig");868const Atom = @import("Atom.zig");
src/link/MachO/Object.zig+1005-488
...@@ -1,16 +1,22 @@...@@ -1,16 +1,22 @@
1archive: ?InArchive = null,1/// Non-zero for fat object files or archives
2offset: u64,
2path: []const u8,3path: []const u8,
3file_handle: File.HandleIndex,4file_handle: File.HandleIndex,
4mtime: u64,5mtime: u64,
5index: File.Index,6index: File.Index,
7in_archive: ?InArchive = null,
68
7header: ?macho.mach_header_64 = null,9header: ?macho.mach_header_64 = null,
8sections: std.MultiArrayList(Section) = .{},10sections: std.MultiArrayList(Section) = .{},
9symtab: std.MultiArrayList(Nlist) = .{},11symtab: std.MultiArrayList(Nlist) = .{},
10strtab: std.ArrayListUnmanaged(u8) = .{},12strtab: std.ArrayListUnmanaged(u8) = .{},
1113
12symbols: std.ArrayListUnmanaged(Symbol.Index) = .{},14symbols: std.ArrayListUnmanaged(Symbol) = .{},
13atoms: std.ArrayListUnmanaged(Atom.Index) = .{},15symbols_extra: std.ArrayListUnmanaged(u32) = .{},
16globals: std.ArrayListUnmanaged(MachO.SymbolResolver.Index) = .{},
17atoms: std.ArrayListUnmanaged(Atom) = .{},
18atoms_indexes: std.ArrayListUnmanaged(Atom.Index) = .{},
19atoms_extra: std.ArrayListUnmanaged(u32) = .{},
1420
15platform: ?MachO.Platform = null,21platform: ?MachO.Platform = null,
16compile_unit: ?CompileUnit = null,22compile_unit: ?CompileUnit = null,
...@@ -21,13 +27,14 @@ compact_unwind_sect_index: ?u8 = null,...@@ -21,13 +27,14 @@ compact_unwind_sect_index: ?u8 = null,
21cies: std.ArrayListUnmanaged(Cie) = .{},27cies: std.ArrayListUnmanaged(Cie) = .{},
22fdes: std.ArrayListUnmanaged(Fde) = .{},28fdes: std.ArrayListUnmanaged(Fde) = .{},
23eh_frame_data: std.ArrayListUnmanaged(u8) = .{},29eh_frame_data: std.ArrayListUnmanaged(u8) = .{},
24unwind_records: std.ArrayListUnmanaged(UnwindInfo.Record.Index) = .{},30unwind_records: std.ArrayListUnmanaged(UnwindInfo.Record) = .{},
31unwind_records_indexes: std.ArrayListUnmanaged(UnwindInfo.Record.Index) = .{},
25data_in_code: std.ArrayListUnmanaged(macho.data_in_code_entry) = .{},32data_in_code: std.ArrayListUnmanaged(macho.data_in_code_entry) = .{},
2633
27alive: bool = true,34alive: bool = true,
28hidden: bool = false,35hidden: bool = false,
2936
30dynamic_relocs: MachO.DynamicRelocs = .{},37compact_unwind_ctx: CompactUnwindCtx = .{},
31output_symtab_ctx: MachO.SymtabCtx = .{},38output_symtab_ctx: MachO.SymtabCtx = .{},
32output_ar_state: Archive.ArState = .{},39output_ar_state: Archive.ArState = .{},
3340
...@@ -39,7 +46,7 @@ pub fn isObject(path: []const u8) !bool {...@@ -39,7 +46,7 @@ pub fn isObject(path: []const u8) !bool {
39}46}
4047
41pub fn deinit(self: *Object, allocator: Allocator) void {48pub fn deinit(self: *Object, allocator: Allocator) void {
42 if (self.archive) |*ar| allocator.free(ar.path);49 if (self.in_archive) |*ar| allocator.free(ar.path);
43 allocator.free(self.path);50 allocator.free(self.path);
44 for (self.sections.items(.relocs), self.sections.items(.subsections)) |*relocs, *sub| {51 for (self.sections.items(.relocs), self.sections.items(.subsections)) |*relocs, *sub| {
45 relocs.deinit(allocator);52 relocs.deinit(allocator);
...@@ -49,11 +56,16 @@ pub fn deinit(self: *Object, allocator: Allocator) void {...@@ -49,11 +56,16 @@ pub fn deinit(self: *Object, allocator: Allocator) void {
49 self.symtab.deinit(allocator);56 self.symtab.deinit(allocator);
50 self.strtab.deinit(allocator);57 self.strtab.deinit(allocator);
51 self.symbols.deinit(allocator);58 self.symbols.deinit(allocator);
59 self.symbols_extra.deinit(allocator);
60 self.globals.deinit(allocator);
52 self.atoms.deinit(allocator);61 self.atoms.deinit(allocator);
62 self.atoms_indexes.deinit(allocator);
63 self.atoms_extra.deinit(allocator);
53 self.cies.deinit(allocator);64 self.cies.deinit(allocator);
54 self.fdes.deinit(allocator);65 self.fdes.deinit(allocator);
55 self.eh_frame_data.deinit(allocator);66 self.eh_frame_data.deinit(allocator);
56 self.unwind_records.deinit(allocator);67 self.unwind_records.deinit(allocator);
68 self.unwind_records_indexes.deinit(allocator);
57 for (self.stab_files.items) |*sf| {69 for (self.stab_files.items) |*sf| {
58 sf.stabs.deinit(allocator);70 sf.stabs.deinit(allocator);
59 }71 }
...@@ -65,13 +77,18 @@ pub fn parse(self: *Object, macho_file: *MachO) !void {...@@ -65,13 +77,18 @@ pub fn parse(self: *Object, macho_file: *MachO) !void {
65 const tracy = trace(@src());77 const tracy = trace(@src());
66 defer tracy.end();78 defer tracy.end();
6779
80 log.debug("parsing {}", .{self.fmtPath()});
81
68 const gpa = macho_file.base.comp.gpa;82 const gpa = macho_file.base.comp.gpa;
69 const offset = if (self.archive) |ar| ar.offset else 0;
70 const handle = macho_file.getFileHandle(self.file_handle);83 const handle = macho_file.getFileHandle(self.file_handle);
84 const cpu_arch = macho_file.getTarget().cpu.arch;
85
86 // Atom at index 0 is reserved as null atom
87 try self.atoms.append(gpa, .{ .extra = try self.addAtomExtra(gpa, .{}) });
7188
72 var header_buffer: [@sizeOf(macho.mach_header_64)]u8 = undefined;89 var header_buffer: [@sizeOf(macho.mach_header_64)]u8 = undefined;
73 {90 {
74 const amt = try handle.preadAll(&header_buffer, offset);91 const amt = try handle.preadAll(&header_buffer, self.offset);
75 if (amt != @sizeOf(macho.mach_header_64)) return error.InputOutput;92 if (amt != @sizeOf(macho.mach_header_64)) return error.InputOutput;
76 }93 }
77 self.header = @as(*align(1) const macho.mach_header_64, @ptrCast(&header_buffer)).*;94 self.header = @as(*align(1) const macho.mach_header_64, @ptrCast(&header_buffer)).*;
...@@ -84,7 +101,7 @@ pub fn parse(self: *Object, macho_file: *MachO) !void {...@@ -84,7 +101,7 @@ pub fn parse(self: *Object, macho_file: *MachO) !void {
84 return error.InvalidCpuArch;101 return error.InvalidCpuArch;
85 },102 },
86 };103 };
87 if (macho_file.getTarget().cpu.arch != this_cpu_arch) {104 if (cpu_arch != this_cpu_arch) {
88 try macho_file.reportParseError2(self.index, "invalid cpu architecture: {s}", .{@tagName(this_cpu_arch)});105 try macho_file.reportParseError2(self.index, "invalid cpu architecture: {s}", .{@tagName(this_cpu_arch)});
89 return error.InvalidCpuArch;106 return error.InvalidCpuArch;
90 }107 }
...@@ -92,7 +109,7 @@ pub fn parse(self: *Object, macho_file: *MachO) !void {...@@ -92,7 +109,7 @@ pub fn parse(self: *Object, macho_file: *MachO) !void {
92 const lc_buffer = try gpa.alloc(u8, self.header.?.sizeofcmds);109 const lc_buffer = try gpa.alloc(u8, self.header.?.sizeofcmds);
93 defer gpa.free(lc_buffer);110 defer gpa.free(lc_buffer);
94 {111 {
95 const amt = try handle.preadAll(lc_buffer, offset + @sizeOf(macho.mach_header_64));112 const amt = try handle.preadAll(lc_buffer, self.offset + @sizeOf(macho.mach_header_64));
96 if (amt != self.header.?.sizeofcmds) return error.InputOutput;113 if (amt != self.header.?.sizeofcmds) return error.InputOutput;
97 }114 }
98115
...@@ -119,14 +136,14 @@ pub fn parse(self: *Object, macho_file: *MachO) !void {...@@ -119,14 +136,14 @@ pub fn parse(self: *Object, macho_file: *MachO) !void {
119 const cmd = lc.cast(macho.symtab_command).?;136 const cmd = lc.cast(macho.symtab_command).?;
120 try self.strtab.resize(gpa, cmd.strsize);137 try self.strtab.resize(gpa, cmd.strsize);
121 {138 {
122 const amt = try handle.preadAll(self.strtab.items, cmd.stroff + offset);139 const amt = try handle.preadAll(self.strtab.items, cmd.stroff + self.offset);
123 if (amt != self.strtab.items.len) return error.InputOutput;140 if (amt != self.strtab.items.len) return error.InputOutput;
124 }141 }
125142
126 const symtab_buffer = try gpa.alloc(u8, cmd.nsyms * @sizeOf(macho.nlist_64));143 const symtab_buffer = try gpa.alloc(u8, cmd.nsyms * @sizeOf(macho.nlist_64));
127 defer gpa.free(symtab_buffer);144 defer gpa.free(symtab_buffer);
128 {145 {
129 const amt = try handle.preadAll(symtab_buffer, cmd.symoff + offset);146 const amt = try handle.preadAll(symtab_buffer, cmd.symoff + self.offset);
130 if (amt != symtab_buffer.len) return error.InputOutput;147 if (amt != symtab_buffer.len) return error.InputOutput;
131 }148 }
132 const symtab = @as([*]align(1) const macho.nlist_64, @ptrCast(symtab_buffer.ptr))[0..cmd.nsyms];149 const symtab = @as([*]align(1) const macho.nlist_64, @ptrCast(symtab_buffer.ptr))[0..cmd.nsyms];
...@@ -144,7 +161,7 @@ pub fn parse(self: *Object, macho_file: *MachO) !void {...@@ -144,7 +161,7 @@ pub fn parse(self: *Object, macho_file: *MachO) !void {
144 const buffer = try gpa.alloc(u8, cmd.datasize);161 const buffer = try gpa.alloc(u8, cmd.datasize);
145 defer gpa.free(buffer);162 defer gpa.free(buffer);
146 {163 {
147 const amt = try handle.preadAll(buffer, offset + cmd.dataoff);164 const amt = try handle.preadAll(buffer, self.offset + cmd.dataoff);
148 if (amt != buffer.len) return error.InputOutput;165 if (amt != buffer.len) return error.InputOutput;
149 }166 }
150 const ndice = @divExact(cmd.datasize, @sizeOf(macho.data_in_code_entry));167 const ndice = @divExact(cmd.datasize, @sizeOf(macho.data_in_code_entry));
...@@ -196,39 +213,39 @@ pub fn parse(self: *Object, macho_file: *MachO) !void {...@@ -196,39 +213,39 @@ pub fn parse(self: *Object, macho_file: *MachO) !void {
196 mem.sort(NlistIdx, nlists.items, self, NlistIdx.lessThan);213 mem.sort(NlistIdx, nlists.items, self, NlistIdx.lessThan);
197214
198 if (self.hasSubsections()) {215 if (self.hasSubsections()) {
199 try self.initSubsections(nlists.items, macho_file);216 try self.initSubsections(gpa, nlists.items);
200 } else {217 } else {
201 try self.initSections(nlists.items, macho_file);218 try self.initSections(gpa, nlists.items);
202 }219 }
203220
204 try self.initCstringLiterals(macho_file);221 try self.initCstringLiterals(gpa, handle, macho_file);
205 try self.initFixedSizeLiterals(macho_file);222 try self.initFixedSizeLiterals(gpa, macho_file);
206 try self.initPointerLiterals(macho_file);223 try self.initPointerLiterals(gpa, macho_file);
207 try self.linkNlistToAtom(macho_file);224 try self.linkNlistToAtom(macho_file);
208225
209 try self.sortAtoms(macho_file);226 try self.sortAtoms(macho_file);
210 try self.initSymbols(macho_file);227 try self.initSymbols(gpa, macho_file);
211 try self.initSymbolStabs(nlists.items, macho_file);228 try self.initSymbolStabs(gpa, nlists.items, macho_file);
212 try self.initRelocs(macho_file);229 try self.initRelocs(handle, cpu_arch, macho_file);
213230
214 // Parse DWARF __TEXT,__eh_frame section231 // Parse DWARF __TEXT,__eh_frame section
215 if (self.eh_frame_sect_index) |index| {232 if (self.eh_frame_sect_index) |index| {
216 try self.initEhFrameRecords(index, macho_file);233 try self.initEhFrameRecords(gpa, index, handle, macho_file);
217 }234 }
218235
219 // Parse Apple's __LD,__compact_unwind section236 // Parse Apple's __LD,__compact_unwind section
220 if (self.compact_unwind_sect_index) |index| {237 if (self.compact_unwind_sect_index) |index| {
221 try self.initUnwindRecords(index, macho_file);238 try self.initUnwindRecords(gpa, index, handle, macho_file);
222 }239 }
223240
224 if (self.hasUnwindRecords() or self.hasEhFrameRecords()) {241 if (self.hasUnwindRecords() or self.hasEhFrameRecords()) {
225 try self.parseUnwindRecords(macho_file);242 try self.parseUnwindRecords(gpa, cpu_arch, macho_file);
226 }243 }
227244
228 if (self.platform) |platform| {245 if (self.platform) |platform| {
229 if (!macho_file.platform.eqlTarget(platform)) {246 if (!macho_file.platform.eqlTarget(platform)) {
230 try macho_file.reportParseError2(self.index, "invalid platform: {}", .{247 try macho_file.reportParseError2(self.index, "invalid platform: {}", .{
231 platform.fmtTarget(macho_file.getTarget().cpu.arch),248 platform.fmtTarget(cpu_arch),
232 });249 });
233 return error.InvalidTarget;250 return error.InvalidTarget;
234 }251 }
...@@ -244,8 +261,10 @@ pub fn parse(self: *Object, macho_file: *MachO) !void {...@@ -244,8 +261,10 @@ pub fn parse(self: *Object, macho_file: *MachO) !void {
244 // }261 // }
245 }262 }
246263
247 for (self.atoms.items) |atom_index| {264 try self.parseDebugInfo(macho_file);
248 const atom = macho_file.getAtom(atom_index).?;265
266 for (self.getAtoms()) |atom_index| {
267 const atom = self.getAtom(atom_index) orelse continue;
249 const isec = atom.getInputSection(macho_file);268 const isec = atom.getInputSection(macho_file);
250 if (mem.eql(u8, isec.sectName(), "__eh_frame") or269 if (mem.eql(u8, isec.sectName(), "__eh_frame") or
251 mem.eql(u8, isec.sectName(), "__compact_unwind") or270 mem.eql(u8, isec.sectName(), "__compact_unwind") or
...@@ -274,10 +293,9 @@ pub fn isPtrLiteral(sect: macho.section_64) bool {...@@ -274,10 +293,9 @@ pub fn isPtrLiteral(sect: macho.section_64) bool {
274 return sect.type() == macho.S_LITERAL_POINTERS;293 return sect.type() == macho.S_LITERAL_POINTERS;
275}294}
276295
277fn initSubsections(self: *Object, nlists: anytype, macho_file: *MachO) !void {296fn initSubsections(self: *Object, allocator: Allocator, nlists: anytype) !void {
278 const tracy = trace(@src());297 const tracy = trace(@src());
279 defer tracy.end();298 defer tracy.end();
280 const gpa = macho_file.base.comp.gpa;
281 const slice = self.sections.slice();299 const slice = self.sections.slice();
282 for (slice.items(.header), slice.items(.subsections), 0..) |sect, *subsections, n_sect| {300 for (slice.items(.header), slice.items(.subsections), 0..) |sect, *subsections, n_sect| {
283 if (isCstringLiteral(sect)) continue;301 if (isCstringLiteral(sect)) continue;
...@@ -292,17 +310,18 @@ fn initSubsections(self: *Object, nlists: anytype, macho_file: *MachO) !void {...@@ -292,17 +310,18 @@ fn initSubsections(self: *Object, nlists: anytype, macho_file: *MachO) !void {
292 } else nlists.len;310 } else nlists.len;
293311
294 if (nlist_start == nlist_end or nlists[nlist_start].nlist.n_value > sect.addr) {312 if (nlist_start == nlist_end or nlists[nlist_start].nlist.n_value > sect.addr) {
295 const name = try std.fmt.allocPrintZ(gpa, "{s}${s}", .{ sect.segName(), sect.sectName() });313 const name = try std.fmt.allocPrintZ(allocator, "{s}${s}", .{ sect.segName(), sect.sectName() });
296 defer gpa.free(name);314 defer allocator.free(name);
297 const size = if (nlist_start == nlist_end) sect.size else nlists[nlist_start].nlist.n_value - sect.addr;315 const size = if (nlist_start == nlist_end) sect.size else nlists[nlist_start].nlist.n_value - sect.addr;
298 const atom_index = try self.addAtom(.{316 const atom_index = try self.addAtom(allocator, .{
299 .name = try self.addString(gpa, name),317 .name = try self.addString(allocator, name),
300 .n_sect = @intCast(n_sect),318 .n_sect = @intCast(n_sect),
301 .off = 0,319 .off = 0,
302 .size = size,320 .size = size,
303 .alignment = sect.@"align",321 .alignment = sect.@"align",
304 }, macho_file);322 });
305 try subsections.append(gpa, .{323 try self.atoms_indexes.append(allocator, atom_index);
324 try subsections.append(allocator, .{
306 .atom = atom_index,325 .atom = atom_index,
307 .off = 0,326 .off = 0,
308 });327 });
...@@ -325,14 +344,15 @@ fn initSubsections(self: *Object, nlists: anytype, macho_file: *MachO) !void {...@@ -325,14 +344,15 @@ fn initSubsections(self: *Object, nlists: anytype, macho_file: *MachO) !void {
325 @min(@ctz(nlist.nlist.n_value), sect.@"align")344 @min(@ctz(nlist.nlist.n_value), sect.@"align")
326 else345 else
327 sect.@"align";346 sect.@"align";
328 const atom_index = try self.addAtom(.{347 const atom_index = try self.addAtom(allocator, .{
329 .name = nlist.nlist.n_strx,348 .name = nlist.nlist.n_strx,
330 .n_sect = @intCast(n_sect),349 .n_sect = @intCast(n_sect),
331 .off = nlist.nlist.n_value - sect.addr,350 .off = nlist.nlist.n_value - sect.addr,
332 .size = size,351 .size = size,
333 .alignment = alignment,352 .alignment = alignment,
334 }, macho_file);353 });
335 try subsections.append(gpa, .{354 try self.atoms_indexes.append(allocator, atom_index);
355 try subsections.append(allocator, .{
336 .atom = atom_index,356 .atom = atom_index,
337 .off = nlist.nlist.n_value - sect.addr,357 .off = nlist.nlist.n_value - sect.addr,
338 });358 });
...@@ -344,30 +364,31 @@ fn initSubsections(self: *Object, nlists: anytype, macho_file: *MachO) !void {...@@ -344,30 +364,31 @@ fn initSubsections(self: *Object, nlists: anytype, macho_file: *MachO) !void {
344 }364 }
345}365}
346366
347fn initSections(self: *Object, nlists: anytype, macho_file: *MachO) !void {367fn initSections(self: *Object, allocator: Allocator, nlists: anytype) !void {
348 const tracy = trace(@src());368 const tracy = trace(@src());
349 defer tracy.end();369 defer tracy.end();
350 const gpa = macho_file.base.comp.gpa;
351 const slice = self.sections.slice();370 const slice = self.sections.slice();
352371
353 try self.atoms.ensureUnusedCapacity(gpa, self.sections.items(.header).len);372 try self.atoms.ensureUnusedCapacity(allocator, self.sections.items(.header).len);
373 try self.atoms_indexes.ensureUnusedCapacity(allocator, self.sections.items(.header).len);
354374
355 for (slice.items(.header), 0..) |sect, n_sect| {375 for (slice.items(.header), 0..) |sect, n_sect| {
356 if (isCstringLiteral(sect)) continue;376 if (isCstringLiteral(sect)) continue;
357 if (isFixedSizeLiteral(sect)) continue;377 if (isFixedSizeLiteral(sect)) continue;
358 if (isPtrLiteral(sect)) continue;378 if (isPtrLiteral(sect)) continue;
359379
360 const name = try std.fmt.allocPrintZ(gpa, "{s}${s}", .{ sect.segName(), sect.sectName() });380 const name = try std.fmt.allocPrintZ(allocator, "{s}${s}", .{ sect.segName(), sect.sectName() });
361 defer gpa.free(name);381 defer allocator.free(name);
362382
363 const atom_index = try self.addAtom(.{383 const atom_index = try self.addAtom(allocator, .{
364 .name = try self.addString(gpa, name),384 .name = try self.addString(allocator, name),
365 .n_sect = @intCast(n_sect),385 .n_sect = @intCast(n_sect),
366 .off = 0,386 .off = 0,
367 .size = sect.size,387 .size = sect.size,
368 .alignment = sect.@"align",388 .alignment = sect.@"align",
369 }, macho_file);389 });
370 try slice.items(.subsections)[n_sect].append(gpa, .{ .atom = atom_index, .off = 0 });390 try self.atoms_indexes.append(allocator, atom_index);
391 try slice.items(.subsections)[n_sect].append(allocator, .{ .atom = atom_index, .off = 0 });
371392
372 const nlist_start = for (nlists, 0..) |nlist, i| {393 const nlist_start = for (nlists, 0..) |nlist, i| {
373 if (nlist.nlist.n_sect - 1 == n_sect) break i;394 if (nlist.nlist.n_sect - 1 == n_sect) break i;
...@@ -396,21 +417,25 @@ fn initSections(self: *Object, nlists: anytype, macho_file: *MachO) !void {...@@ -396,21 +417,25 @@ fn initSections(self: *Object, nlists: anytype, macho_file: *MachO) !void {
396 }417 }
397}418}
398419
399fn initCstringLiterals(self: *Object, macho_file: *MachO) !void {420fn initCstringLiterals(self: *Object, allocator: Allocator, file: File.Handle, macho_file: *MachO) !void {
400 const tracy = trace(@src());421 const tracy = trace(@src());
401 defer tracy.end();422 defer tracy.end();
402423
403 const gpa = macho_file.base.comp.gpa;
404 const slice = self.sections.slice();424 const slice = self.sections.slice();
405425
406 for (slice.items(.header), 0..) |sect, n_sect| {426 for (slice.items(.header), 0..) |sect, n_sect| {
407 if (!isCstringLiteral(sect)) continue;427 if (!isCstringLiteral(sect)) continue;
408428
409 const data = try self.getSectionData(@intCast(n_sect), macho_file);429 const sect_size = math.cast(usize, sect.size) orelse return error.Overflow;
410 defer gpa.free(data);430 const data = try allocator.alloc(u8, sect_size);
431 defer allocator.free(data);
432 const amt = try file.preadAll(data, sect.offset + self.offset);
433 if (amt != data.len) return error.InputOutput;
411434
435 var count: u32 = 0;
412 var start: u32 = 0;436 var start: u32 = 0;
413 while (start < data.len) {437 while (start < data.len) {
438 defer count += 1;
414 var end = start;439 var end = start;
415 while (end < data.len - 1 and data[end] != 0) : (end += 1) {}440 while (end < data.len - 1 and data[end] != 0) : (end += 1) {}
416 if (data[end] != 0) {441 if (data[end] != 0) {
...@@ -423,32 +448,52 @@ fn initCstringLiterals(self: *Object, macho_file: *MachO) !void {...@@ -423,32 +448,52 @@ fn initCstringLiterals(self: *Object, macho_file: *MachO) !void {
423 }448 }
424 end += 1;449 end += 1;
425450
426 const atom_index = try self.addAtom(.{451 const name = try std.fmt.allocPrintZ(allocator, "l._str{d}", .{count});
427 .name = 0,452 defer allocator.free(name);
453 const name_str = try self.addString(allocator, name);
454
455 const atom_index = try self.addAtom(allocator, .{
456 .name = name_str,
428 .n_sect = @intCast(n_sect),457 .n_sect = @intCast(n_sect),
429 .off = start,458 .off = start,
430 .size = end - start,459 .size = end - start,
431 .alignment = sect.@"align",460 .alignment = sect.@"align",
432 }, macho_file);461 });
433 try slice.items(.subsections)[n_sect].append(gpa, .{462 try self.atoms_indexes.append(allocator, atom_index);
463 try slice.items(.subsections)[n_sect].append(allocator, .{
434 .atom = atom_index,464 .atom = atom_index,
435 .off = start,465 .off = start,
436 });466 });
437467
468 const atom = self.getAtom(atom_index).?;
469 const nlist_index: u32 = @intCast(try self.symtab.addOne(allocator));
470 self.symtab.set(nlist_index, .{
471 .nlist = .{
472 .n_strx = name_str,
473 .n_type = macho.N_SECT,
474 .n_sect = @intCast(atom.n_sect + 1),
475 .n_desc = 0,
476 .n_value = atom.getInputAddress(macho_file),
477 },
478 .size = atom.size,
479 .atom = atom_index,
480 });
481 atom.addExtra(.{ .literal_symbol_index = nlist_index }, macho_file);
482
438 start = end;483 start = end;
439 }484 }
440 }485 }
441}486}
442487
443fn initFixedSizeLiterals(self: *Object, macho_file: *MachO) !void {488fn initFixedSizeLiterals(self: *Object, allocator: Allocator, macho_file: *MachO) !void {
444 const tracy = trace(@src());489 const tracy = trace(@src());
445 defer tracy.end();490 defer tracy.end();
446491
447 const gpa = macho_file.base.comp.gpa;
448 const slice = self.sections.slice();492 const slice = self.sections.slice();
449493
450 for (slice.items(.header), 0..) |sect, n_sect| {494 for (slice.items(.header), 0..) |sect, n_sect| {
451 if (!isFixedSizeLiteral(sect)) continue;495 if (!isFixedSizeLiteral(sect)) continue;
496
452 const rec_size: u8 = switch (sect.type()) {497 const rec_size: u8 = switch (sect.type()) {
453 macho.S_4BYTE_LITERALS => 4,498 macho.S_4BYTE_LITERALS => 4,
454 macho.S_8BYTE_LITERALS => 8,499 macho.S_8BYTE_LITERALS => 8,
...@@ -463,28 +508,52 @@ fn initFixedSizeLiterals(self: *Object, macho_file: *MachO) !void {...@@ -463,28 +508,52 @@ fn initFixedSizeLiterals(self: *Object, macho_file: *MachO) !void {
463 );508 );
464 return error.MalformedObject;509 return error.MalformedObject;
465 }510 }
511
466 var pos: u32 = 0;512 var pos: u32 = 0;
467 while (pos < sect.size) : (pos += rec_size) {513 var count: u32 = 0;
468 const atom_index = try self.addAtom(.{514 while (pos < sect.size) : ({
469 .name = 0,515 pos += rec_size;
516 count += 1;
517 }) {
518 const name = try std.fmt.allocPrintZ(allocator, "l._literal{d}", .{count});
519 defer allocator.free(name);
520 const name_str = try self.addString(allocator, name);
521
522 const atom_index = try self.addAtom(allocator, .{
523 .name = name_str,
470 .n_sect = @intCast(n_sect),524 .n_sect = @intCast(n_sect),
471 .off = pos,525 .off = pos,
472 .size = rec_size,526 .size = rec_size,
473 .alignment = sect.@"align",527 .alignment = sect.@"align",
474 }, macho_file);528 });
475 try slice.items(.subsections)[n_sect].append(gpa, .{529 try self.atoms_indexes.append(allocator, atom_index);
530 try slice.items(.subsections)[n_sect].append(allocator, .{
476 .atom = atom_index,531 .atom = atom_index,
477 .off = pos,532 .off = pos,
478 });533 });
534
535 const atom = self.getAtom(atom_index).?;
536 const nlist_index: u32 = @intCast(try self.symtab.addOne(allocator));
537 self.symtab.set(nlist_index, .{
538 .nlist = .{
539 .n_strx = name_str,
540 .n_type = macho.N_SECT,
541 .n_sect = @intCast(atom.n_sect + 1),
542 .n_desc = 0,
543 .n_value = atom.getInputAddress(macho_file),
544 },
545 .size = atom.size,
546 .atom = atom_index,
547 });
548 atom.addExtra(.{ .literal_symbol_index = nlist_index }, macho_file);
479 }549 }
480 }550 }
481}551}
482552
483fn initPointerLiterals(self: *Object, macho_file: *MachO) !void {553fn initPointerLiterals(self: *Object, allocator: Allocator, macho_file: *MachO) !void {
484 const tracy = trace(@src());554 const tracy = trace(@src());
485 defer tracy.end();555 defer tracy.end();
486556
487 const gpa = macho_file.base.comp.gpa;
488 const slice = self.sections.slice();557 const slice = self.sections.slice();
489558
490 for (slice.items(.header), 0..) |sect, n_sect| {559 for (slice.items(.header), 0..) |sect, n_sect| {
...@@ -503,134 +572,171 @@ fn initPointerLiterals(self: *Object, macho_file: *MachO) !void {...@@ -503,134 +572,171 @@ fn initPointerLiterals(self: *Object, macho_file: *MachO) !void {
503572
504 for (0..num_ptrs) |i| {573 for (0..num_ptrs) |i| {
505 const pos: u32 = @as(u32, @intCast(i)) * rec_size;574 const pos: u32 = @as(u32, @intCast(i)) * rec_size;
506 const atom_index = try self.addAtom(.{575
507 .name = 0,576 const name = try std.fmt.allocPrintZ(allocator, "l._ptr{d}", .{i});
577 defer allocator.free(name);
578 const name_str = try self.addString(allocator, name);
579
580 const atom_index = try self.addAtom(allocator, .{
581 .name = name_str,
508 .n_sect = @intCast(n_sect),582 .n_sect = @intCast(n_sect),
509 .off = pos,583 .off = pos,
510 .size = rec_size,584 .size = rec_size,
511 .alignment = sect.@"align",585 .alignment = sect.@"align",
512 }, macho_file);586 });
513 try slice.items(.subsections)[n_sect].append(gpa, .{587 try self.atoms_indexes.append(allocator, atom_index);
588 try slice.items(.subsections)[n_sect].append(allocator, .{
514 .atom = atom_index,589 .atom = atom_index,
515 .off = pos,590 .off = pos,
516 });591 });
592
593 const atom = self.getAtom(atom_index).?;
594 const nlist_index: u32 = @intCast(try self.symtab.addOne(allocator));
595 self.symtab.set(nlist_index, .{
596 .nlist = .{
597 .n_strx = name_str,
598 .n_type = macho.N_SECT,
599 .n_sect = @intCast(atom.n_sect + 1),
600 .n_desc = 0,
601 .n_value = atom.getInputAddress(macho_file),
602 },
603 .size = atom.size,
604 .atom = atom_index,
605 });
606 atom.addExtra(.{ .literal_symbol_index = nlist_index }, macho_file);
517 }607 }
518 }608 }
519}609}
520610
521pub fn resolveLiterals(self: Object, lp: *MachO.LiteralPool, macho_file: *MachO) !void {611pub fn resolveLiterals(self: *Object, lp: *MachO.LiteralPool, macho_file: *MachO) !void {
612 const tracy = trace(@src());
613 defer tracy.end();
614
522 const gpa = macho_file.base.comp.gpa;615 const gpa = macho_file.base.comp.gpa;
616 const file = macho_file.getFileHandle(self.file_handle);
523617
524 var buffer = std.ArrayList(u8).init(gpa);618 var buffer = std.ArrayList(u8).init(gpa);
525 defer buffer.deinit();619 defer buffer.deinit();
526620
621 var sections_data = std.AutoHashMap(u32, []const u8).init(gpa);
622 try sections_data.ensureTotalCapacity(@intCast(self.sections.items(.header).len));
623 defer {
624 var it = sections_data.iterator();
625 while (it.next()) |entry| {
626 gpa.free(entry.value_ptr.*);
627 }
628 sections_data.deinit();
629 }
630
527 const slice = self.sections.slice();631 const slice = self.sections.slice();
528 for (slice.items(.header), slice.items(.subsections), 0..) |header, subs, n_sect| {632 for (slice.items(.header), slice.items(.subsections)) |header, subs| {
529 if (isCstringLiteral(header) or isFixedSizeLiteral(header)) {633 if (isCstringLiteral(header) or isFixedSizeLiteral(header)) {
530 const data = try self.getSectionData(@intCast(n_sect), macho_file);634 const sect_size = math.cast(usize, header.size) orelse return error.Overflow;
635 const data = try gpa.alloc(u8, sect_size);
531 defer gpa.free(data);636 defer gpa.free(data);
637 const amt = try file.preadAll(data, header.offset + self.offset);
638 if (amt != data.len) return error.InputOutput;
532639
533 for (subs.items) |sub| {640 for (subs.items) |sub| {
534 const atom = macho_file.getAtom(sub.atom).?;641 const atom = self.getAtom(sub.atom).?;
535 const atom_off = math.cast(usize, atom.off) orelse return error.Overflow;642 const atom_off = math.cast(usize, atom.off) orelse return error.Overflow;
536 const atom_size = math.cast(usize, atom.size) orelse return error.Overflow;643 const atom_size = math.cast(usize, atom.size) orelse return error.Overflow;
537 const atom_data = data[atom_off..][0..atom_size];644 const atom_data = data[atom_off..][0..atom_size];
538 const res = try lp.insert(gpa, header.type(), atom_data);645 const res = try lp.insert(gpa, header.type(), atom_data);
539 if (!res.found_existing) {646 if (!res.found_existing) {
540 res.atom.* = sub.atom;647 res.ref.* = .{ .index = atom.getExtra(macho_file).literal_symbol_index, .file = self.index };
648 } else {
649 const lp_sym = lp.getSymbol(res.index, macho_file);
650 const lp_atom = lp_sym.getAtom(macho_file).?;
651 lp_atom.alignment = lp_atom.alignment.max(atom.alignment);
652 atom.flags.alive = false;
541 }653 }
542 atom.flags.literal_pool = true;654 atom.addExtra(.{ .literal_pool_index = res.index }, macho_file);
543 try atom.addExtra(.{ .literal_index = res.index }, macho_file);
544 }655 }
545 } else if (isPtrLiteral(header)) {656 } else if (isPtrLiteral(header)) {
546 for (subs.items) |sub| {657 for (subs.items) |sub| {
547 const atom = macho_file.getAtom(sub.atom).?;658 const atom = self.getAtom(sub.atom).?;
548 const relocs = atom.getRelocs(macho_file);659 const relocs = atom.getRelocs(macho_file);
549 assert(relocs.len == 1);660 assert(relocs.len == 1);
550 const rel = relocs[0];661 const rel = relocs[0];
551 const target = switch (rel.tag) {662 const target = switch (rel.tag) {
552 .local => rel.target,663 .local => rel.getTargetAtom(atom.*, macho_file),
553 .@"extern" => rel.getTargetSymbol(macho_file).atom,664 .@"extern" => rel.getTargetSymbol(atom.*, macho_file).getAtom(macho_file).?,
554 };665 };
555 const addend = math.cast(u32, rel.addend) orelse return error.Overflow;666 const addend = math.cast(u32, rel.addend) orelse return error.Overflow;
556 const target_atom = macho_file.getAtom(target).?;667 const target_size = math.cast(usize, target.size) orelse return error.Overflow;
557 const target_atom_size = math.cast(usize, target_atom.size) orelse return error.Overflow;668 try buffer.ensureUnusedCapacity(target_size);
558 try buffer.ensureUnusedCapacity(target_atom_size);669 buffer.resize(target_size) catch unreachable;
559 buffer.resize(target_atom_size) catch unreachable;670 const gop = try sections_data.getOrPut(target.n_sect);
560 try target_atom.getData(macho_file, buffer.items);671 if (!gop.found_existing) {
672 const target_sect = slice.items(.header)[target.n_sect];
673 const target_sect_size = math.cast(usize, target_sect.size) orelse return error.Overflow;
674 const data = try gpa.alloc(u8, target_sect_size);
675 const amt = try file.preadAll(data, target_sect.offset + self.offset);
676 if (amt != data.len) return error.InputOutput;
677 gop.value_ptr.* = data;
678 }
679 const data = gop.value_ptr.*;
680 const target_off = math.cast(usize, target.off) orelse return error.Overflow;
681 @memcpy(buffer.items, data[target_off..][0..target_size]);
561 const res = try lp.insert(gpa, header.type(), buffer.items[addend..]);682 const res = try lp.insert(gpa, header.type(), buffer.items[addend..]);
562 buffer.clearRetainingCapacity();683 buffer.clearRetainingCapacity();
563 if (!res.found_existing) {684 if (!res.found_existing) {
564 res.atom.* = sub.atom;685 res.ref.* = .{ .index = atom.getExtra(macho_file).literal_symbol_index, .file = self.index };
686 } else {
687 const lp_sym = lp.getSymbol(res.index, macho_file);
688 const lp_atom = lp_sym.getAtom(macho_file).?;
689 lp_atom.alignment = lp_atom.alignment.max(atom.alignment);
690 atom.flags.alive = false;
565 }691 }
566 atom.flags.literal_pool = true;692 atom.addExtra(.{ .literal_pool_index = res.index }, macho_file);
567 try atom.addExtra(.{ .literal_index = res.index }, macho_file);
568 }693 }
569 }694 }
570 }695 }
571}696}
572697
573pub fn dedupLiterals(self: Object, lp: MachO.LiteralPool, macho_file: *MachO) void {698pub fn dedupLiterals(self: *Object, lp: MachO.LiteralPool, macho_file: *MachO) void {
574 for (self.atoms.items) |atom_index| {699 const tracy = trace(@src());
575 const atom = macho_file.getAtom(atom_index) orelse continue;700 defer tracy.end();
701
702 for (self.getAtoms()) |atom_index| {
703 const atom = self.getAtom(atom_index) orelse continue;
576 if (!atom.flags.alive) continue;704 if (!atom.flags.alive) continue;
577 if (!atom.flags.relocs) continue;
578705
579 const relocs = blk: {706 const relocs = blk: {
580 const extra = atom.getExtra(macho_file).?;707 const extra = atom.getExtra(macho_file);
581 const relocs = self.sections.items(.relocs)[atom.n_sect].items;708 const relocs = self.sections.items(.relocs)[atom.n_sect].items;
582 break :blk relocs[extra.rel_index..][0..extra.rel_count];709 break :blk relocs[extra.rel_index..][0..extra.rel_count];
583 };710 };
584 for (relocs) |*rel| switch (rel.tag) {711 for (relocs) |*rel| {
585 .local => {712 if (rel.tag != .@"extern") continue;
586 const target = macho_file.getAtom(rel.target).?;713 const target_sym_ref = rel.getTargetSymbolRef(atom.*, macho_file);
587 if (target.getLiteralPoolIndex(macho_file)) |lp_index| {714 const file = target_sym_ref.getFile(macho_file) orelse continue;
588 const lp_atom = lp.getAtom(lp_index, macho_file);715 if (file.getIndex() != self.index) continue;
589 if (target.atom_index != lp_atom.atom_index) {716 const target_sym = target_sym_ref.getSymbol(macho_file).?;
590 lp_atom.alignment = lp_atom.alignment.max(target.alignment);717 const target_atom = target_sym.getAtom(macho_file) orelse continue;
591 target.flags.alive = false;718 const isec = target_atom.getInputSection(macho_file);
592 rel.target = lp_atom.atom_index;719 if (!Object.isCstringLiteral(isec) and !Object.isFixedSizeLiteral(isec) and !Object.isPtrLiteral(isec)) continue;
593 }720 const lp_index = target_atom.getExtra(macho_file).literal_pool_index;
594 }721 const lp_sym = lp.getSymbol(lp_index, macho_file);
595 },722 const lp_atom_ref = lp_sym.atom_ref;
596 .@"extern" => {723 if (target_atom.atom_index != lp_atom_ref.index or target_atom.file != lp_atom_ref.file) {
597 const target_sym = rel.getTargetSymbol(macho_file);724 target_sym.atom_ref = lp_atom_ref;
598 if (target_sym.getAtom(macho_file)) |target_atom| {725 }
599 if (target_atom.getLiteralPoolIndex(macho_file)) |lp_index| {726 }
600 const lp_atom = lp.getAtom(lp_index, macho_file);
601 if (target_atom.atom_index != lp_atom.atom_index) {
602 lp_atom.alignment = lp_atom.alignment.max(target_atom.alignment);
603 target_atom.flags.alive = false;
604 target_sym.atom = lp_atom.atom_index;
605 }
606 }
607 }
608 },
609 };
610 }727 }
611}
612728
613const AddAtomArgs = struct {729 for (self.symbols.items) |*sym| {
614 name: u32,730 const atom = sym.getAtom(macho_file) orelse continue;
615 n_sect: u8,731 const isec = atom.getInputSection(macho_file);
616 off: u64,732 if (!Object.isCstringLiteral(isec) and !Object.isFixedSizeLiteral(isec) and !Object.isPtrLiteral(isec)) continue;
617 size: u64,733 const lp_index = atom.getExtra(macho_file).literal_pool_index;
618 alignment: u32,734 const lp_sym = lp.getSymbol(lp_index, macho_file);
619};735 const lp_atom_ref = lp_sym.atom_ref;
620736 if (atom.atom_index != lp_atom_ref.index or self.index != lp_atom_ref.file) {
621fn addAtom(self: *Object, args: AddAtomArgs, macho_file: *MachO) !Atom.Index {737 sym.atom_ref = lp_atom_ref;
622 const gpa = macho_file.base.comp.gpa;738 }
623 const atom_index = try macho_file.addAtom();739 }
624 const atom = macho_file.getAtom(atom_index).?;
625 atom.file = self.index;
626 atom.atom_index = atom_index;
627 atom.name = args.name;
628 atom.n_sect = args.n_sect;
629 atom.size = args.size;
630 atom.alignment = Atom.Alignment.fromLog2Units(args.alignment);
631 atom.off = args.off;
632 try self.atoms.append(gpa, atom_index);
633 return atom_index;
634}740}
635741
636pub fn findAtom(self: Object, addr: u64) ?Atom.Index {742pub fn findAtom(self: Object, addr: u64) ?Atom.Index {
...@@ -702,55 +808,61 @@ fn linkNlistToAtom(self: *Object, macho_file: *MachO) !void {...@@ -702,55 +808,61 @@ fn linkNlistToAtom(self: *Object, macho_file: *MachO) !void {
702 }808 }
703}809}
704810
705fn initSymbols(self: *Object, macho_file: *MachO) !void {811fn initSymbols(self: *Object, allocator: Allocator, macho_file: *MachO) !void {
706 const tracy = trace(@src());812 const tracy = trace(@src());
707 defer tracy.end();813 defer tracy.end();
708 const gpa = macho_file.base.comp.gpa;814
709 const slice = self.symtab.slice();815 const slice = self.symtab.slice();
816 const nsyms = slice.items(.nlist).len;
710817
711 try self.symbols.ensureUnusedCapacity(gpa, slice.items(.nlist).len);818 try self.symbols.ensureTotalCapacityPrecise(allocator, nsyms);
819 try self.symbols_extra.ensureTotalCapacityPrecise(allocator, nsyms * @sizeOf(Symbol.Extra));
820 try self.globals.ensureTotalCapacityPrecise(allocator, nsyms);
821 self.globals.resize(allocator, nsyms) catch unreachable;
822 @memset(self.globals.items, 0);
712823
713 for (slice.items(.nlist), slice.items(.atom), 0..) |nlist, atom_index, i| {824 for (slice.items(.nlist), slice.items(.atom), 0..) |nlist, atom_index, i| {
714 if (nlist.ext()) {825 const index = self.addSymbolAssumeCapacity();
715 const name = self.getString(nlist.n_strx);826 const symbol = &self.symbols.items[index];
716 const off = try macho_file.strings.insert(gpa, name);827 symbol.value = nlist.n_value;
717 const gop = try macho_file.getOrCreateGlobal(off);828 symbol.name = nlist.n_strx;
718 self.symbols.addOneAssumeCapacity().* = gop.index;829 symbol.nlist_idx = @intCast(i);
719 if (nlist.undf() and nlist.weakRef()) {830 symbol.extra = self.addSymbolExtraAssumeCapacity(.{});
720 macho_file.getSymbol(gop.index).flags.weak_ref = true;831
721 }832 if (self.getAtom(atom_index)) |atom| {
722 continue;
723 }
724
725 const index = try macho_file.addSymbol();
726 self.symbols.appendAssumeCapacity(index);
727 const symbol = macho_file.getSymbol(index);
728 symbol.* = .{
729 .value = nlist.n_value,
730 .name = nlist.n_strx,
731 .nlist_idx = @intCast(i),
732 .atom = 0,
733 .file = self.index,
734 };
735
736 if (macho_file.getAtom(atom_index)) |atom| {
737 assert(!nlist.abs());833 assert(!nlist.abs());
738 symbol.value -= atom.getInputAddress(macho_file);834 symbol.value -= atom.getInputAddress(macho_file);
739 symbol.atom = atom_index;835 symbol.atom_ref = .{ .index = atom_index, .file = self.index };
740 }836 }
741837
838 symbol.flags.weak = nlist.weakDef();
742 symbol.flags.abs = nlist.abs();839 symbol.flags.abs = nlist.abs();
840 symbol.flags.tentative = nlist.tentative();
743 symbol.flags.no_dead_strip = symbol.flags.no_dead_strip or nlist.noDeadStrip();841 symbol.flags.no_dead_strip = symbol.flags.no_dead_strip or nlist.noDeadStrip();
842 symbol.flags.dyn_ref = nlist.n_desc & macho.REFERENCED_DYNAMICALLY != 0;
843 symbol.flags.interposable = false;
844 // TODO
845 // symbol.flags.interposable = nlist.ext() and (nlist.sect() or nlist.abs()) and macho_file.base.isDynLib() and macho_file.options.namespace == .flat and !nlist.pext();
744846
745 if (nlist.sect() and847 if (nlist.sect() and
746 self.sections.items(.header)[nlist.n_sect - 1].type() == macho.S_THREAD_LOCAL_VARIABLES)848 self.sections.items(.header)[nlist.n_sect - 1].type() == macho.S_THREAD_LOCAL_VARIABLES)
747 {849 {
748 symbol.flags.tlv = true;850 symbol.flags.tlv = true;
749 }851 }
852
853 if (nlist.ext()) {
854 if (nlist.undf()) {
855 symbol.flags.weak_ref = nlist.weakRef();
856 } else if (nlist.pext() or (nlist.weakDef() and nlist.weakRef()) or self.hidden) {
857 symbol.visibility = .hidden;
858 } else {
859 symbol.visibility = .global;
860 }
861 }
750 }862 }
751}863}
752864
753fn initSymbolStabs(self: *Object, nlists: anytype, macho_file: *MachO) !void {865fn initSymbolStabs(self: *Object, allocator: Allocator, nlists: anytype, macho_file: *MachO) !void {
754 const tracy = trace(@src());866 const tracy = trace(@src());
755 defer tracy.end();867 defer tracy.end();
756868
...@@ -761,7 +873,7 @@ fn initSymbolStabs(self: *Object, nlists: anytype, macho_file: *MachO) !void {...@@ -761,7 +873,7 @@ fn initSymbolStabs(self: *Object, nlists: anytype, macho_file: *MachO) !void {
761 fn find(fs: @This(), addr: u64) ?Symbol.Index {873 fn find(fs: @This(), addr: u64) ?Symbol.Index {
762 // TODO binary search since we have the list sorted874 // TODO binary search since we have the list sorted
763 for (fs.entries) |nlist| {875 for (fs.entries) |nlist| {
764 if (nlist.nlist.n_value == addr) return fs.ctx.symbols.items[nlist.idx];876 if (nlist.nlist.n_value == addr) return @intCast(nlist.idx);
765 }877 }
766 return null;878 return null;
767 }879 }
...@@ -776,14 +888,13 @@ fn initSymbolStabs(self: *Object, nlists: anytype, macho_file: *MachO) !void {...@@ -776,14 +888,13 @@ fn initSymbolStabs(self: *Object, nlists: anytype, macho_file: *MachO) !void {
776888
777 if (start == end) return;889 if (start == end) return;
778890
779 const gpa = macho_file.base.comp.gpa;
780 const syms = self.symtab.items(.nlist);891 const syms = self.symtab.items(.nlist);
781 const sym_lookup = SymbolLookup{ .ctx = self, .entries = nlists };892 const sym_lookup = SymbolLookup{ .ctx = self, .entries = nlists };
782893
783 // We need to cache nlists by name so that we can properly resolve local N_GSYM stabs.894 // We need to cache nlists by name so that we can properly resolve local N_GSYM stabs.
784 // What happens is `ld -r` will emit an N_GSYM stab for a symbol that may be either an895 // What happens is `ld -r` will emit an N_GSYM stab for a symbol that may be either an
785 // external or private external.896 // external or private external.
786 var addr_lookup = std.StringHashMap(u64).init(gpa);897 var addr_lookup = std.StringHashMap(u64).init(allocator);
787 defer addr_lookup.deinit();898 defer addr_lookup.deinit();
788 for (syms) |sym| {899 for (syms) |sym| {
789 if (sym.sect() and (sym.ext() or sym.pext())) {900 if (sym.sect() and (sym.ext() or sym.pext())) {
...@@ -813,17 +924,17 @@ fn initSymbolStabs(self: *Object, nlists: anytype, macho_file: *MachO) !void {...@@ -813,17 +924,17 @@ fn initSymbolStabs(self: *Object, nlists: anytype, macho_file: *MachO) !void {
813 switch (nlist.n_type) {924 switch (nlist.n_type) {
814 macho.N_BNSYM => {925 macho.N_BNSYM => {
815 stab.is_func = true;926 stab.is_func = true;
816 stab.symbol = sym_lookup.find(nlist.n_value);927 stab.index = sym_lookup.find(nlist.n_value);
817 // TODO validate928 // TODO validate
818 i += 3;929 i += 3;
819 },930 },
820 macho.N_GSYM => {931 macho.N_GSYM => {
821 stab.is_func = false;932 stab.is_func = false;
822 stab.symbol = sym_lookup.find(addr_lookup.get(self.getString(nlist.n_strx)).?);933 stab.index = sym_lookup.find(addr_lookup.get(self.getString(nlist.n_strx)).?);
823 },934 },
824 macho.N_STSYM => {935 macho.N_STSYM => {
825 stab.is_func = false;936 stab.is_func = false;
826 stab.symbol = sym_lookup.find(nlist.n_value);937 stab.index = sym_lookup.find(nlist.n_value);
827 },938 },
828 else => {939 else => {
829 try macho_file.reportParseError2(self.index, "unhandled symbol stab type 0x{x}", .{940 try macho_file.reportParseError2(self.index, "unhandled symbol stab type 0x{x}", .{
...@@ -832,29 +943,35 @@ fn initSymbolStabs(self: *Object, nlists: anytype, macho_file: *MachO) !void {...@@ -832,29 +943,35 @@ fn initSymbolStabs(self: *Object, nlists: anytype, macho_file: *MachO) !void {
832 return error.MalformedObject;943 return error.MalformedObject;
833 },944 },
834 }945 }
835 try sf.stabs.append(gpa, stab);946 try sf.stabs.append(allocator, stab);
836 }947 }
837948
838 try self.stab_files.append(gpa, sf);949 try self.stab_files.append(allocator, sf);
839 }950 }
840}951}
841952
842fn sortAtoms(self: *Object, macho_file: *MachO) !void {953fn sortAtoms(self: *Object, macho_file: *MachO) !void {
843 const lessThanAtom = struct {954 const Ctx = struct {
844 fn lessThanAtom(ctx: *MachO, lhs: Atom.Index, rhs: Atom.Index) bool {955 object: *Object,
845 return ctx.getAtom(lhs).?.getInputAddress(ctx) < ctx.getAtom(rhs).?.getInputAddress(ctx);956 mfile: *MachO,
957
958 fn lessThanAtom(ctx: @This(), lhs: Atom.Index, rhs: Atom.Index) bool {
959 return ctx.object.getAtom(lhs).?.getInputAddress(ctx.mfile) <
960 ctx.object.getAtom(rhs).?.getInputAddress(ctx.mfile);
846 }961 }
847 }.lessThanAtom;962 };
848 mem.sort(Atom.Index, self.atoms.items, macho_file, lessThanAtom);963 mem.sort(Atom.Index, self.atoms_indexes.items, Ctx{
964 .object = self,
965 .mfile = macho_file,
966 }, Ctx.lessThanAtom);
849}967}
850968
851fn initRelocs(self: *Object, macho_file: *MachO) !void {969fn initRelocs(self: *Object, file: File.Handle, cpu_arch: std.Target.Cpu.Arch, macho_file: *MachO) !void {
852 const tracy = trace(@src());970 const tracy = trace(@src());
853 defer tracy.end();971 defer tracy.end();
854 const cpu_arch = macho_file.getTarget().cpu.arch;
855 const slice = self.sections.slice();972 const slice = self.sections.slice();
856973
857 for (slice.items(.header), slice.items(.relocs), 0..) |sect, *out, n_sect| {974 for (slice.items(.header), slice.items(.relocs)) |sect, *out| {
858 if (sect.nreloc == 0) continue;975 if (sect.nreloc == 0) continue;
859 // We skip relocs for __DWARF since even in -r mode, the linker is expected to emit976 // We skip relocs for __DWARF since even in -r mode, the linker is expected to emit
860 // debug symbol stabs in the relocatable. This made me curious why that is. For now,977 // debug symbol stabs in the relocatable. This made me curious why that is. For now,
...@@ -863,8 +980,8 @@ fn initRelocs(self: *Object, macho_file: *MachO) !void {...@@ -863,8 +980,8 @@ fn initRelocs(self: *Object, macho_file: *MachO) !void {
863 !mem.eql(u8, sect.sectName(), "__compact_unwind")) continue;980 !mem.eql(u8, sect.sectName(), "__compact_unwind")) continue;
864981
865 switch (cpu_arch) {982 switch (cpu_arch) {
866 .x86_64 => try x86_64.parseRelocs(self, @intCast(n_sect), sect, out, macho_file),983 .x86_64 => try x86_64.parseRelocs(self, sect, out, file, macho_file),
867 .aarch64 => try aarch64.parseRelocs(self, @intCast(n_sect), sect, out, macho_file),984 .aarch64 => try aarch64.parseRelocs(self, sect, out, file, macho_file),
868 else => unreachable,985 else => unreachable,
869 }986 }
870987
...@@ -876,7 +993,7 @@ fn initRelocs(self: *Object, macho_file: *MachO) !void {...@@ -876,7 +993,7 @@ fn initRelocs(self: *Object, macho_file: *MachO) !void {
876993
877 var next_reloc: u32 = 0;994 var next_reloc: u32 = 0;
878 for (subsections.items) |subsection| {995 for (subsections.items) |subsection| {
879 const atom = macho_file.getAtom(subsection.atom).?;996 const atom = self.getAtom(subsection.atom).?;
880 if (!atom.flags.alive) continue;997 if (!atom.flags.alive) continue;
881 if (next_reloc >= relocs.items.len) break;998 if (next_reloc >= relocs.items.len) break;
882 const end_addr = atom.off + atom.size;999 const end_addr = atom.off + atom.size;
...@@ -885,27 +1002,23 @@ fn initRelocs(self: *Object, macho_file: *MachO) !void {...@@ -885,27 +1002,23 @@ fn initRelocs(self: *Object, macho_file: *MachO) !void {
885 while (next_reloc < relocs.items.len and relocs.items[next_reloc].offset < end_addr) : (next_reloc += 1) {}1002 while (next_reloc < relocs.items.len and relocs.items[next_reloc].offset < end_addr) : (next_reloc += 1) {}
8861003
887 const rel_count = next_reloc - rel_index;1004 const rel_count = next_reloc - rel_index;
888 try atom.addExtra(.{ .rel_index = rel_index, .rel_count = rel_count }, macho_file);1005 atom.addExtra(.{ .rel_index = @intCast(rel_index), .rel_count = @intCast(rel_count) }, macho_file);
889 atom.flags.relocs = true;
890 }1006 }
891 }1007 }
892}1008}
8931009
894fn initEhFrameRecords(self: *Object, sect_id: u8, macho_file: *MachO) !void {1010fn initEhFrameRecords(self: *Object, allocator: Allocator, sect_id: u8, file: File.Handle, macho_file: *MachO) !void {
895 const tracy = trace(@src());1011 const tracy = trace(@src());
896 defer tracy.end();1012 defer tracy.end();
897 const gpa = macho_file.base.comp.gpa;
898 const nlists = self.symtab.items(.nlist);1013 const nlists = self.symtab.items(.nlist);
899 const slice = self.sections.slice();1014 const slice = self.sections.slice();
900 const sect = slice.items(.header)[sect_id];1015 const sect = slice.items(.header)[sect_id];
901 const relocs = slice.items(.relocs)[sect_id];1016 const relocs = slice.items(.relocs)[sect_id];
9021017
903 // TODO: read into buffer directly1018 const size = math.cast(usize, sect.size) orelse return error.Overflow;
904 const data = try self.getSectionData(sect_id, macho_file);1019 try self.eh_frame_data.resize(allocator, size);
905 defer gpa.free(data);1020 const amt = try file.preadAll(self.eh_frame_data.items, sect.offset + self.offset);
9061021 if (amt != self.eh_frame_data.items.len) return error.InputOutput;
907 try self.eh_frame_data.ensureTotalCapacityPrecise(gpa, data.len);
908 self.eh_frame_data.appendSliceAssumeCapacity(data);
9091022
910 // Check for non-personality relocs in FDEs and apply them1023 // Check for non-personality relocs in FDEs and apply them
911 for (relocs.items, 0..) |rel, i| {1024 for (relocs.items, 0..) |rel, i| {
...@@ -937,12 +1050,12 @@ fn initEhFrameRecords(self: *Object, sect_id: u8, macho_file: *MachO) !void {...@@ -937,12 +1050,12 @@ fn initEhFrameRecords(self: *Object, sect_id: u8, macho_file: *MachO) !void {
937 var it = eh_frame.Iterator{ .data = self.eh_frame_data.items };1050 var it = eh_frame.Iterator{ .data = self.eh_frame_data.items };
938 while (try it.next()) |rec| {1051 while (try it.next()) |rec| {
939 switch (rec.tag) {1052 switch (rec.tag) {
940 .cie => try self.cies.append(gpa, .{1053 .cie => try self.cies.append(allocator, .{
941 .offset = rec.offset,1054 .offset = rec.offset,
942 .size = rec.size,1055 .size = rec.size,
943 .file = self.index,1056 .file = self.index,
944 }),1057 }),
945 .fde => try self.fdes.append(gpa, .{1058 .fde => try self.fdes.append(allocator, .{
946 .offset = rec.offset,1059 .offset = rec.offset,
947 .size = rec.size,1060 .size = rec.size,
948 .cie = undefined,1061 .cie = undefined,
...@@ -987,7 +1100,7 @@ fn initEhFrameRecords(self: *Object, sect_id: u8, macho_file: *MachO) !void {...@@ -987,7 +1100,7 @@ fn initEhFrameRecords(self: *Object, sect_id: u8, macho_file: *MachO) !void {
987 }1100 }
988}1101}
9891102
990fn initUnwindRecords(self: *Object, sect_id: u8, macho_file: *MachO) !void {1103fn initUnwindRecords(self: *Object, allocator: Allocator, sect_id: u8, file: File.Handle, macho_file: *MachO) !void {
991 const tracy = trace(@src());1104 const tracy = trace(@src());
992 defer tracy.end();1105 defer tracy.end();
9931106
...@@ -995,27 +1108,31 @@ fn initUnwindRecords(self: *Object, sect_id: u8, macho_file: *MachO) !void {...@@ -995,27 +1108,31 @@ fn initUnwindRecords(self: *Object, sect_id: u8, macho_file: *MachO) !void {
995 ctx: *const Object,1108 ctx: *const Object,
9961109
997 fn find(fs: @This(), addr: u64) ?Symbol.Index {1110 fn find(fs: @This(), addr: u64) ?Symbol.Index {
998 for (fs.ctx.symbols.items, 0..) |sym_index, i| {1111 for (0..fs.ctx.symbols.items.len) |i| {
999 const nlist = fs.ctx.symtab.items(.nlist)[i];1112 const nlist = fs.ctx.symtab.items(.nlist)[i];
1000 if (nlist.ext() and nlist.n_value == addr) return sym_index;1113 if (nlist.ext() and nlist.n_value == addr) return @intCast(i);
1001 }1114 }
1002 return null;1115 return null;
1003 }1116 }
1004 };1117 };
10051118
1006 const gpa = macho_file.base.comp.gpa;1119 const header = self.sections.items(.header)[sect_id];
1007 const data = try self.getSectionData(sect_id, macho_file);1120 const size = math.cast(usize, header.size) orelse return error.Overflow;
1008 defer gpa.free(data);1121 const data = try allocator.alloc(u8, size);
1122 defer allocator.free(data);
1123 const amt = try file.preadAll(data, header.offset + self.offset);
1124 if (amt != data.len) return error.InputOutput;
1125
1009 const nrecs = @divExact(data.len, @sizeOf(macho.compact_unwind_entry));1126 const nrecs = @divExact(data.len, @sizeOf(macho.compact_unwind_entry));
1010 const recs = @as([*]align(1) const macho.compact_unwind_entry, @ptrCast(data.ptr))[0..nrecs];1127 const recs = @as([*]align(1) const macho.compact_unwind_entry, @ptrCast(data.ptr))[0..nrecs];
1011 const sym_lookup = SymbolLookup{ .ctx = self };1128 const sym_lookup = SymbolLookup{ .ctx = self };
10121129
1013 try self.unwind_records.resize(gpa, nrecs);1130 try self.unwind_records.ensureTotalCapacityPrecise(allocator, nrecs);
1131 try self.unwind_records_indexes.ensureTotalCapacityPrecise(allocator, nrecs);
10141132
1015 const header = self.sections.items(.header)[sect_id];
1016 const relocs = self.sections.items(.relocs)[sect_id].items;1133 const relocs = self.sections.items(.relocs)[sect_id].items;
1017 var reloc_idx: usize = 0;1134 var reloc_idx: usize = 0;
1018 for (recs, self.unwind_records.items, 0..) |rec, *out_index, rec_idx| {1135 for (recs, 0..) |rec, rec_idx| {
1019 const rec_start = rec_idx * @sizeOf(macho.compact_unwind_entry);1136 const rec_start = rec_idx * @sizeOf(macho.compact_unwind_entry);
1020 const rec_end = rec_start + @sizeOf(macho.compact_unwind_entry);1137 const rec_end = rec_start + @sizeOf(macho.compact_unwind_entry);
1021 const reloc_start = reloc_idx;1138 const reloc_start = reloc_idx;
...@@ -1023,11 +1140,11 @@ fn initUnwindRecords(self: *Object, sect_id: u8, macho_file: *MachO) !void {...@@ -1023,11 +1140,11 @@ fn initUnwindRecords(self: *Object, sect_id: u8, macho_file: *MachO) !void {
1023 relocs[reloc_idx].offset < rec_end) : (reloc_idx += 1)1140 relocs[reloc_idx].offset < rec_end) : (reloc_idx += 1)
1024 {}1141 {}
10251142
1026 out_index.* = try macho_file.addUnwindRecord();1143 const out_index = self.addUnwindRecordAssumeCapacity();
1027 const out = macho_file.getUnwindRecord(out_index.*);1144 self.unwind_records_indexes.appendAssumeCapacity(out_index);
1145 const out = self.getUnwindRecord(out_index);
1028 out.length = rec.rangeLength;1146 out.length = rec.rangeLength;
1029 out.enc = .{ .enc = rec.compactUnwindEncoding };1147 out.enc = .{ .enc = rec.compactUnwindEncoding };
1030 out.file = self.index;
10311148
1032 for (relocs[reloc_start..reloc_idx]) |rel| {1149 for (relocs[reloc_start..reloc_idx]) |rel| {
1033 if (rel.type != .unsigned or rel.meta.length != 3) {1150 if (rel.type != .unsigned or rel.meta.length != 3) {
...@@ -1090,7 +1207,7 @@ fn initUnwindRecords(self: *Object, sect_id: u8, macho_file: *MachO) !void {...@@ -1090,7 +1207,7 @@ fn initUnwindRecords(self: *Object, sect_id: u8, macho_file: *MachO) !void {
1090 }1207 }
1091}1208}
10921209
1093fn parseUnwindRecords(self: *Object, macho_file: *MachO) !void {1210fn parseUnwindRecords(self: *Object, allocator: Allocator, cpu_arch: std.Target.Cpu.Arch, macho_file: *MachO) !void {
1094 // Synthesise missing unwind records.1211 // Synthesise missing unwind records.
1095 // The logic here is as follows:1212 // The logic here is as follows:
1096 // 1. if an atom has unwind info record that is not DWARF, FDE is marked dead1213 // 1. if an atom has unwind info record that is not DWARF, FDE is marked dead
...@@ -1100,8 +1217,7 @@ fn parseUnwindRecords(self: *Object, macho_file: *MachO) !void {...@@ -1100,8 +1217,7 @@ fn parseUnwindRecords(self: *Object, macho_file: *MachO) !void {
11001217
1101 const Superposition = struct { atom: Atom.Index, size: u64, cu: ?UnwindInfo.Record.Index = null, fde: ?Fde.Index = null };1218 const Superposition = struct { atom: Atom.Index, size: u64, cu: ?UnwindInfo.Record.Index = null, fde: ?Fde.Index = null };
11021219
1103 const gpa = macho_file.base.comp.gpa;1220 var superposition = std.AutoArrayHashMap(u64, Superposition).init(allocator);
1104 var superposition = std.AutoArrayHashMap(u64, Superposition).init(gpa);
1105 defer superposition.deinit();1221 defer superposition.deinit();
11061222
1107 const slice = self.symtab.slice();1223 const slice = self.symtab.slice();
...@@ -1119,8 +1235,8 @@ fn parseUnwindRecords(self: *Object, macho_file: *MachO) !void {...@@ -1119,8 +1235,8 @@ fn parseUnwindRecords(self: *Object, macho_file: *MachO) !void {
1119 }1235 }
1120 }1236 }
11211237
1122 for (self.unwind_records.items) |rec_index| {1238 for (self.unwind_records_indexes.items) |rec_index| {
1123 const rec = macho_file.getUnwindRecord(rec_index);1239 const rec = self.getUnwindRecord(rec_index);
1124 const atom = rec.getAtom(macho_file);1240 const atom = rec.getAtom(macho_file);
1125 const addr = atom.getInputAddress(macho_file) + rec.atom_offset;1241 const addr = atom.getInputAddress(macho_file) + rec.atom_offset;
1126 superposition.getPtr(addr).?.cu = rec_index;1242 superposition.getPtr(addr).?.cu = rec_index;
...@@ -1137,7 +1253,7 @@ fn parseUnwindRecords(self: *Object, macho_file: *MachO) !void {...@@ -1137,7 +1253,7 @@ fn parseUnwindRecords(self: *Object, macho_file: *MachO) !void {
1137 const fde = &self.fdes.items[fde_index];1253 const fde = &self.fdes.items[fde_index];
11381254
1139 if (meta.cu) |rec_index| {1255 if (meta.cu) |rec_index| {
1140 const rec = macho_file.getUnwindRecord(rec_index);1256 const rec = self.getUnwindRecord(rec_index);
1141 if (!rec.enc.isDwarf(macho_file)) {1257 if (!rec.enc.isDwarf(macho_file)) {
1142 // Mark FDE dead1258 // Mark FDE dead
1143 fde.alive = false;1259 fde.alive = false;
...@@ -1147,15 +1263,14 @@ fn parseUnwindRecords(self: *Object, macho_file: *MachO) !void {...@@ -1147,15 +1263,14 @@ fn parseUnwindRecords(self: *Object, macho_file: *MachO) !void {
1147 }1263 }
1148 } else {1264 } else {
1149 // Synthesise new unwind info record1265 // Synthesise new unwind info record
1150 const rec_index = try macho_file.addUnwindRecord();1266 const rec_index = try self.addUnwindRecord(allocator);
1151 const rec = macho_file.getUnwindRecord(rec_index);1267 const rec = self.getUnwindRecord(rec_index);
1152 try self.unwind_records.append(gpa, rec_index);1268 try self.unwind_records_indexes.append(allocator, rec_index);
1153 rec.length = @intCast(meta.size);1269 rec.length = @intCast(meta.size);
1154 rec.atom = fde.atom;1270 rec.atom = fde.atom;
1155 rec.atom_offset = fde.atom_offset;1271 rec.atom_offset = fde.atom_offset;
1156 rec.fde = fde_index;1272 rec.fde = fde_index;
1157 rec.file = fde.file;1273 switch (cpu_arch) {
1158 switch (macho_file.getTarget().cpu.arch) {
1159 .x86_64 => rec.enc.setMode(macho.UNWIND_X86_64_MODE.DWARF),1274 .x86_64 => rec.enc.setMode(macho.UNWIND_X86_64_MODE.DWARF),
1160 .aarch64 => rec.enc.setMode(macho.UNWIND_ARM64_MODE.DWARF),1275 .aarch64 => rec.enc.setMode(macho.UNWIND_ARM64_MODE.DWARF),
1161 else => unreachable,1276 else => unreachable,
...@@ -1163,10 +1278,10 @@ fn parseUnwindRecords(self: *Object, macho_file: *MachO) !void {...@@ -1163,10 +1278,10 @@ fn parseUnwindRecords(self: *Object, macho_file: *MachO) !void {
1163 }1278 }
1164 } else if (meta.cu == null and meta.fde == null) {1279 } else if (meta.cu == null and meta.fde == null) {
1165 // Create a null record1280 // Create a null record
1166 const rec_index = try macho_file.addUnwindRecord();1281 const rec_index = try self.addUnwindRecord(allocator);
1167 const rec = macho_file.getUnwindRecord(rec_index);1282 const rec = self.getUnwindRecord(rec_index);
1168 const atom = macho_file.getAtom(meta.atom).?;1283 const atom = self.getAtom(meta.atom).?;
1169 try self.unwind_records.append(gpa, rec_index);1284 try self.unwind_records_indexes.append(allocator, rec_index);
1170 rec.length = @intCast(meta.size);1285 rec.length = @intCast(meta.size);
1171 rec.atom = meta.atom;1286 rec.atom = meta.atom;
1172 rec.atom_offset = @intCast(addr - atom.getInputAddress(macho_file));1287 rec.atom_offset = @intCast(addr - atom.getInputAddress(macho_file));
...@@ -1174,30 +1289,35 @@ fn parseUnwindRecords(self: *Object, macho_file: *MachO) !void {...@@ -1174,30 +1289,35 @@ fn parseUnwindRecords(self: *Object, macho_file: *MachO) !void {
1174 }1289 }
1175 }1290 }
11761291
1177 const sortFn = struct {1292 const SortCtx = struct {
1178 fn sortFn(ctx: *MachO, lhs_index: UnwindInfo.Record.Index, rhs_index: UnwindInfo.Record.Index) bool {1293 object: *Object,
1179 const lhs = ctx.getUnwindRecord(lhs_index);1294 mfile: *MachO,
1180 const rhs = ctx.getUnwindRecord(rhs_index);1295
1181 const lhsa = lhs.getAtom(ctx);1296 fn sort(ctx: @This(), lhs_index: UnwindInfo.Record.Index, rhs_index: UnwindInfo.Record.Index) bool {
1182 const rhsa = rhs.getAtom(ctx);1297 const lhs = ctx.object.getUnwindRecord(lhs_index);
1183 return lhsa.getInputAddress(ctx) + lhs.atom_offset < rhsa.getInputAddress(ctx) + rhs.atom_offset;1298 const rhs = ctx.object.getUnwindRecord(rhs_index);
1299 const lhsa = lhs.getAtom(ctx.mfile);
1300 const rhsa = rhs.getAtom(ctx.mfile);
1301 return lhsa.getInputAddress(ctx.mfile) + lhs.atom_offset < rhsa.getInputAddress(ctx.mfile) + rhs.atom_offset;
1184 }1302 }
1185 }.sortFn;1303 };
1186 mem.sort(UnwindInfo.Record.Index, self.unwind_records.items, macho_file, sortFn);1304 mem.sort(UnwindInfo.Record.Index, self.unwind_records_indexes.items, SortCtx{
1305 .object = self,
1306 .mfile = macho_file,
1307 }, SortCtx.sort);
11871308
1188 // Associate unwind records to atoms1309 // Associate unwind records to atoms
1189 var next_cu: u32 = 0;1310 var next_cu: u32 = 0;
1190 while (next_cu < self.unwind_records.items.len) {1311 while (next_cu < self.unwind_records_indexes.items.len) {
1191 const start = next_cu;1312 const start = next_cu;
1192 const rec_index = self.unwind_records.items[start];1313 const rec_index = self.unwind_records_indexes.items[start];
1193 const rec = macho_file.getUnwindRecord(rec_index);1314 const rec = self.getUnwindRecord(rec_index);
1194 while (next_cu < self.unwind_records.items.len and1315 while (next_cu < self.unwind_records_indexes.items.len and
1195 macho_file.getUnwindRecord(self.unwind_records.items[next_cu]).atom == rec.atom) : (next_cu += 1)1316 self.getUnwindRecord(self.unwind_records_indexes.items[next_cu]).atom == rec.atom) : (next_cu += 1)
1196 {}1317 {}
11971318
1198 const atom = rec.getAtom(macho_file);1319 const atom = rec.getAtom(macho_file);
1199 try atom.addExtra(.{ .unwind_index = start, .unwind_count = next_cu - start }, macho_file);1320 atom.addExtra(.{ .unwind_index = start, .unwind_count = next_cu - start }, macho_file);
1200 atom.flags.unwind = true;
1201 }1321 }
1202}1322}
12031323
...@@ -1205,7 +1325,7 @@ fn parseUnwindRecords(self: *Object, macho_file: *MachO) !void {...@@ -1205,7 +1325,7 @@ fn parseUnwindRecords(self: *Object, macho_file: *MachO) !void {
1205/// and record that so that we can emit symbol stabs.1325/// and record that so that we can emit symbol stabs.
1206/// TODO in the future, we want parse debug info and debug line sections so that1326/// TODO in the future, we want parse debug info and debug line sections so that
1207/// we can provide nice error locations to the user.1327/// we can provide nice error locations to the user.
1208pub fn parseDebugInfo(self: *Object, macho_file: *MachO) !void {1328fn parseDebugInfo(self: *Object, macho_file: *MachO) !void {
1209 const tracy = trace(@src());1329 const tracy = trace(@src());
1210 defer tracy.end();1330 defer tracy.end();
12111331
...@@ -1224,11 +1344,34 @@ pub fn parseDebugInfo(self: *Object, macho_file: *MachO) !void {...@@ -1224,11 +1344,34 @@ pub fn parseDebugInfo(self: *Object, macho_file: *MachO) !void {
12241344
1225 if (debug_info_index == null or debug_abbrev_index == null) return;1345 if (debug_info_index == null or debug_abbrev_index == null) return;
12261346
1227 const debug_info = try self.getSectionData(@intCast(debug_info_index.?), macho_file);1347 const slice = self.sections.slice();
1348 const file = macho_file.getFileHandle(self.file_handle);
1349 const debug_info = blk: {
1350 const sect = slice.items(.header)[debug_info_index.?];
1351 const size = math.cast(usize, sect.size) orelse return error.Overflow;
1352 const data = try gpa.alloc(u8, size);
1353 const amt = try file.preadAll(data, sect.offset + self.offset);
1354 if (amt != data.len) return error.InputOutput;
1355 break :blk data;
1356 };
1228 defer gpa.free(debug_info);1357 defer gpa.free(debug_info);
1229 const debug_abbrev = try self.getSectionData(@intCast(debug_abbrev_index.?), macho_file);1358 const debug_abbrev = blk: {
1359 const sect = slice.items(.header)[debug_abbrev_index.?];
1360 const size = math.cast(usize, sect.size) orelse return error.Overflow;
1361 const data = try gpa.alloc(u8, size);
1362 const amt = try file.preadAll(data, sect.offset + self.offset);
1363 if (amt != data.len) return error.InputOutput;
1364 break :blk data;
1365 };
1230 defer gpa.free(debug_abbrev);1366 defer gpa.free(debug_abbrev);
1231 const debug_str = if (debug_str_index) |index| try self.getSectionData(@intCast(index), macho_file) else &[0]u8{};1367 const debug_str = if (debug_str_index) |sid| blk: {
1368 const sect = slice.items(.header)[sid];
1369 const size = math.cast(usize, sect.size) orelse return error.Overflow;
1370 const data = try gpa.alloc(u8, size);
1371 const amt = try file.preadAll(data, sect.offset + self.offset);
1372 if (amt != data.len) return error.InputOutput;
1373 break :blk data;
1374 } else &[0]u8{};
1232 defer gpa.free(debug_str);1375 defer gpa.free(debug_str);
12331376
1234 self.compile_unit = self.findCompileUnit(.{1377 self.compile_unit = self.findCompileUnit(.{
...@@ -1334,87 +1477,55 @@ fn findCompileUnit(self: *Object, args: struct {...@@ -1334,87 +1477,55 @@ fn findCompileUnit(self: *Object, args: struct {
1334 };1477 };
1335}1478}
13361479
1337pub fn resolveSymbols(self: *Object, macho_file: *MachO) void {1480pub fn resolveSymbols(self: *Object, macho_file: *MachO) !void {
1338 const tracy = trace(@src());1481 const tracy = trace(@src());
1339 defer tracy.end();1482 defer tracy.end();
13401483
1341 for (self.symbols.items, 0..) |index, i| {1484 const gpa = macho_file.base.comp.gpa;
1342 const nlist_idx = @as(Symbol.Index, @intCast(i));
1343 const nlist = self.symtab.items(.nlist)[nlist_idx];
1344 const atom_index = self.symtab.items(.atom)[nlist_idx];
13451485
1486 for (self.symtab.items(.nlist), self.symtab.items(.atom), self.globals.items, 0..) |nlist, atom_index, *global, i| {
1346 if (!nlist.ext()) continue;1487 if (!nlist.ext()) continue;
1347 if (nlist.undf() and !nlist.tentative()) continue;
1348 if (nlist.sect()) {1488 if (nlist.sect()) {
1349 const atom = macho_file.getAtom(atom_index).?;1489 const atom = self.getAtom(atom_index).?;
1350 if (!atom.flags.alive) continue;1490 if (!atom.flags.alive) continue;
1351 }1491 }
13521492
1353 const symbol = macho_file.getSymbol(index);1493 const gop = try macho_file.resolver.getOrPut(gpa, .{
1494 .index = @intCast(i),
1495 .file = self.index,
1496 }, macho_file);
1497 if (!gop.found_existing) {
1498 gop.ref.* = .{ .index = 0, .file = 0 };
1499 }
1500 global.* = gop.index;
1501
1502 if (nlist.undf() and !nlist.tentative()) continue;
1503 if (gop.ref.getFile(macho_file) == null) {
1504 gop.ref.* = .{ .index = @intCast(i), .file = self.index };
1505 continue;
1506 }
1507
1354 if (self.asFile().getSymbolRank(.{1508 if (self.asFile().getSymbolRank(.{
1355 .archive = !self.alive,1509 .archive = !self.alive,
1356 .weak = nlist.weakDef(),1510 .weak = nlist.weakDef(),
1357 .tentative = nlist.tentative(),1511 .tentative = nlist.tentative(),
1358 }) < symbol.getSymbolRank(macho_file)) {1512 }) < gop.ref.getSymbol(macho_file).?.getSymbolRank(macho_file)) {
1359 const value = if (nlist.sect()) blk: {1513 gop.ref.* = .{ .index = @intCast(i), .file = self.index };
1360 const atom = macho_file.getAtom(atom_index).?;
1361 break :blk nlist.n_value - atom.getInputAddress(macho_file);
1362 } else nlist.n_value;
1363 symbol.value = value;
1364 symbol.atom = atom_index;
1365 symbol.nlist_idx = nlist_idx;
1366 symbol.file = self.index;
1367 symbol.flags.weak = nlist.weakDef();
1368 symbol.flags.abs = nlist.abs();
1369 symbol.flags.tentative = nlist.tentative();
1370 symbol.flags.weak_ref = false;
1371 symbol.flags.dyn_ref = nlist.n_desc & macho.REFERENCED_DYNAMICALLY != 0;
1372 symbol.flags.no_dead_strip = symbol.flags.no_dead_strip or nlist.noDeadStrip();
1373 // TODO: symbol.flags.interposable = macho_file.base.isDynLib() and macho_file.options.namespace == .flat and !nlist.pext();
1374 symbol.flags.interposable = false;
1375
1376 if (nlist.sect() and
1377 self.sections.items(.header)[nlist.n_sect - 1].type() == macho.S_THREAD_LOCAL_VARIABLES)
1378 {
1379 symbol.flags.tlv = true;
1380 }
1381 }
1382
1383 // Regardless of who the winner is, we still merge symbol visibility here.
1384 if (nlist.pext() or (nlist.weakDef() and nlist.weakRef()) or self.hidden) {
1385 if (symbol.visibility != .global) {
1386 symbol.visibility = .hidden;
1387 }
1388 } else {
1389 symbol.visibility = .global;
1390 }1514 }
1391 }1515 }
1392}1516}
13931517
1394pub fn resetGlobals(self: *Object, macho_file: *MachO) void {
1395 for (self.symbols.items, 0..) |sym_index, nlist_idx| {
1396 if (!self.symtab.items(.nlist)[nlist_idx].ext()) continue;
1397 const sym = macho_file.getSymbol(sym_index);
1398 const name = sym.name;
1399 const global = sym.flags.global;
1400 const weak_ref = sym.flags.weak_ref;
1401 sym.* = .{};
1402 sym.name = name;
1403 sym.flags.global = global;
1404 sym.flags.weak_ref = weak_ref;
1405 }
1406}
1407
1408pub fn markLive(self: *Object, macho_file: *MachO) void {1518pub fn markLive(self: *Object, macho_file: *MachO) void {
1409 const tracy = trace(@src());1519 const tracy = trace(@src());
1410 defer tracy.end();1520 defer tracy.end();
14111521
1412 for (self.symbols.items, 0..) |index, nlist_idx| {1522 for (0..self.symbols.items.len) |i| {
1413 const nlist = self.symtab.items(.nlist)[nlist_idx];1523 const nlist = self.symtab.items(.nlist)[i];
1414 if (!nlist.ext()) continue;1524 if (!nlist.ext()) continue;
14151525
1416 const sym = macho_file.getSymbol(index);1526 const ref = self.getSymbolRef(@intCast(i), macho_file);
1417 const file = sym.getFile(macho_file) orelse continue;1527 const file = ref.getFile(macho_file) orelse continue;
1528 const sym = ref.getSymbol(macho_file).?;
1418 const should_keep = nlist.undf() or (nlist.tentative() and !sym.flags.tentative);1529 const should_keep = nlist.undf() or (nlist.tentative() and !sym.flags.tentative);
1419 if (should_keep and file == .object and !file.object.alive) {1530 if (should_keep and file == .object and !file.object.alive) {
1420 file.object.alive = true;1531 file.object.alive = true;
...@@ -1423,38 +1534,36 @@ pub fn markLive(self: *Object, macho_file: *MachO) void {...@@ -1423,38 +1534,36 @@ pub fn markLive(self: *Object, macho_file: *MachO) void {
1423 }1534 }
1424}1535}
14251536
1426pub fn checkDuplicates(self: *Object, dupes: anytype, macho_file: *MachO) error{OutOfMemory}!void {1537pub fn mergeSymbolVisibility(self: *Object, macho_file: *MachO) void {
1427 for (self.symbols.items, 0..) |index, nlist_idx| {1538 const tracy = trace(@src());
1428 const sym = macho_file.getSymbol(index);1539 defer tracy.end();
1429 if (sym.visibility != .global) continue;
1430 const file = sym.getFile(macho_file) orelse continue;
1431 if (file.getIndex() == self.index) continue;
14321540
1433 const nlist = self.symtab.items(.nlist)[nlist_idx];1541 for (self.symbols.items, 0..) |sym, i| {
1434 if (!nlist.undf() and !nlist.tentative() and !(nlist.weakDef() or nlist.pext())) {1542 const ref = self.getSymbolRef(@intCast(i), macho_file);
1435 const gop = try dupes.getOrPut(index);1543 const global = ref.getSymbol(macho_file) orelse continue;
1436 if (!gop.found_existing) {1544 if (sym.visibility.rank() < global.visibility.rank()) {
1437 gop.value_ptr.* = .{};1545 global.visibility = sym.visibility;
1438 }1546 }
1439 try gop.value_ptr.append(macho_file.base.comp.gpa, self.index);1547 if (sym.flags.weak_ref) {
1548 global.flags.weak_ref = true;
1440 }1549 }
1441 }1550 }
1442}1551}
14431552
1444pub fn scanRelocs(self: Object, macho_file: *MachO) !void {1553pub fn scanRelocs(self: *Object, macho_file: *MachO) !void {
1445 const tracy = trace(@src());1554 const tracy = trace(@src());
1446 defer tracy.end();1555 defer tracy.end();
14471556
1448 for (self.atoms.items) |atom_index| {1557 for (self.getAtoms()) |atom_index| {
1449 const atom = macho_file.getAtom(atom_index).?;1558 const atom = self.getAtom(atom_index) orelse continue;
1450 if (!atom.flags.alive) continue;1559 if (!atom.flags.alive) continue;
1451 const sect = atom.getInputSection(macho_file);1560 const sect = atom.getInputSection(macho_file);
1452 if (sect.isZerofill()) continue;1561 if (sect.isZerofill()) continue;
1453 try atom.scanRelocs(macho_file);1562 try atom.scanRelocs(macho_file);
1454 }1563 }
14551564
1456 for (self.unwind_records.items) |rec_index| {1565 for (self.unwind_records_indexes.items) |rec_index| {
1457 const rec = macho_file.getUnwindRecord(rec_index);1566 const rec = self.getUnwindRecord(rec_index);
1458 if (!rec.alive) continue;1567 if (!rec.alive) continue;
1459 if (rec.getFde(macho_file)) |fde| {1568 if (rec.getFde(macho_file)) |fde| {
1460 if (fde.getCie(macho_file).getPersonality(macho_file)) |sym| {1569 if (fde.getCie(macho_file).getPersonality(macho_file)) |sym| {
...@@ -1471,38 +1580,35 @@ pub fn convertTentativeDefinitions(self: *Object, macho_file: *MachO) !void {...@@ -1471,38 +1580,35 @@ pub fn convertTentativeDefinitions(self: *Object, macho_file: *MachO) !void {
1471 defer tracy.end();1580 defer tracy.end();
1472 const gpa = macho_file.base.comp.gpa;1581 const gpa = macho_file.base.comp.gpa;
14731582
1474 for (self.symbols.items, 0..) |index, i| {1583 for (self.symbols.items, self.globals.items, 0..) |*sym, off, i| {
1475 const sym = macho_file.getSymbol(index);
1476 if (!sym.flags.tentative) continue;1584 if (!sym.flags.tentative) continue;
1477 const sym_file = sym.getFile(macho_file).?;1585 if (macho_file.resolver.get(off).?.file != self.index) continue;
1478 if (sym_file.getIndex() != self.index) continue;
14791586
1480 const nlist_idx = @as(Symbol.Index, @intCast(i));1587 const nlist_idx = @as(Symbol.Index, @intCast(i));
1481 const nlist = &self.symtab.items(.nlist)[nlist_idx];1588 const nlist = &self.symtab.items(.nlist)[nlist_idx];
1482 const nlist_atom = &self.symtab.items(.atom)[nlist_idx];1589 const nlist_atom = &self.symtab.items(.atom)[nlist_idx];
14831590
1484 const atom_index = try macho_file.addAtom();
1485 try self.atoms.append(gpa, atom_index);
1486
1487 const name = try std.fmt.allocPrintZ(gpa, "__DATA$__common${s}", .{sym.getName(macho_file)});1591 const name = try std.fmt.allocPrintZ(gpa, "__DATA$__common${s}", .{sym.getName(macho_file)});
1488 defer gpa.free(name);1592 defer gpa.free(name);
1489 const atom = macho_file.getAtom(atom_index).?;
1490 atom.atom_index = atom_index;
1491 atom.name = try self.addString(gpa, name);
1492 atom.file = self.index;
1493 atom.size = nlist.n_value;
1494 atom.alignment = Atom.Alignment.fromLog2Units((nlist.n_desc >> 8) & 0x0f);
14951593
1594 const alignment = (nlist.n_desc >> 8) & 0x0f;
1496 const n_sect = try self.addSection(gpa, "__DATA", "__common");1595 const n_sect = try self.addSection(gpa, "__DATA", "__common");
1596 const atom_index = try self.addAtom(gpa, .{
1597 .name = try self.addString(gpa, name),
1598 .n_sect = n_sect,
1599 .off = 0,
1600 .size = nlist.n_value,
1601 .alignment = alignment,
1602 });
1603 try self.atoms_indexes.append(gpa, atom_index);
1604
1497 const sect = &self.sections.items(.header)[n_sect];1605 const sect = &self.sections.items(.header)[n_sect];
1498 sect.flags = macho.S_ZEROFILL;1606 sect.flags = macho.S_ZEROFILL;
1499 sect.size = atom.size;1607 sect.size = nlist.n_value;
1500 sect.@"align" = atom.alignment.toLog2Units();1608 sect.@"align" = alignment;
1501 atom.n_sect = n_sect;
15021609
1503 sym.value = 0;1610 sym.value = 0;
1504 sym.atom = atom_index;1611 sym.atom_ref = .{ .index = atom_index, .file = self.index };
1505 sym.flags.global = true;
1506 sym.flags.weak = false;1612 sym.flags.weak = false;
1507 sym.flags.weak_ref = false;1613 sym.flags.weak_ref = false;
1508 sym.flags.tentative = false;1614 sym.flags.tentative = false;
...@@ -1516,8 +1622,8 @@ pub fn convertTentativeDefinitions(self: *Object, macho_file: *MachO) !void {...@@ -1516,8 +1622,8 @@ pub fn convertTentativeDefinitions(self: *Object, macho_file: *MachO) !void {
1516 }1622 }
1517}1623}
15181624
1519fn addSection(self: *Object, allocator: Allocator, segname: []const u8, sectname: []const u8) !u32 {1625fn addSection(self: *Object, allocator: Allocator, segname: []const u8, sectname: []const u8) !u8 {
1520 const n_sect = @as(u32, @intCast(try self.sections.addOne(allocator)));1626 const n_sect = @as(u8, @intCast(try self.sections.addOne(allocator)));
1521 self.sections.set(n_sect, .{1627 self.sections.set(n_sect, .{
1522 .header = .{1628 .header = .{
1523 .sectname = MachO.makeStaticString(sectname),1629 .sectname = MachO.makeStaticString(sectname),
...@@ -1532,12 +1638,11 @@ pub fn parseAr(self: *Object, macho_file: *MachO) !void {...@@ -1532,12 +1638,11 @@ pub fn parseAr(self: *Object, macho_file: *MachO) !void {
1532 defer tracy.end();1638 defer tracy.end();
15331639
1534 const gpa = macho_file.base.comp.gpa;1640 const gpa = macho_file.base.comp.gpa;
1535 const offset = if (self.archive) |ar| ar.offset else 0;
1536 const handle = macho_file.getFileHandle(self.file_handle);1641 const handle = macho_file.getFileHandle(self.file_handle);
15371642
1538 var header_buffer: [@sizeOf(macho.mach_header_64)]u8 = undefined;1643 var header_buffer: [@sizeOf(macho.mach_header_64)]u8 = undefined;
1539 {1644 {
1540 const amt = try handle.preadAll(&header_buffer, offset);1645 const amt = try handle.preadAll(&header_buffer, self.offset);
1541 if (amt != @sizeOf(macho.mach_header_64)) return error.InputOutput;1646 if (amt != @sizeOf(macho.mach_header_64)) return error.InputOutput;
1542 }1647 }
1543 self.header = @as(*align(1) const macho.mach_header_64, @ptrCast(&header_buffer)).*;1648 self.header = @as(*align(1) const macho.mach_header_64, @ptrCast(&header_buffer)).*;
...@@ -1558,7 +1663,7 @@ pub fn parseAr(self: *Object, macho_file: *MachO) !void {...@@ -1558,7 +1663,7 @@ pub fn parseAr(self: *Object, macho_file: *MachO) !void {
1558 const lc_buffer = try gpa.alloc(u8, self.header.?.sizeofcmds);1663 const lc_buffer = try gpa.alloc(u8, self.header.?.sizeofcmds);
1559 defer gpa.free(lc_buffer);1664 defer gpa.free(lc_buffer);
1560 {1665 {
1561 const amt = try handle.preadAll(lc_buffer, offset + @sizeOf(macho.mach_header_64));1666 const amt = try handle.preadAll(lc_buffer, self.offset + @sizeOf(macho.mach_header_64));
1562 if (amt != self.header.?.sizeofcmds) return error.InputOutput;1667 if (amt != self.header.?.sizeofcmds) return error.InputOutput;
1563 }1668 }
15641669
...@@ -1571,14 +1676,14 @@ pub fn parseAr(self: *Object, macho_file: *MachO) !void {...@@ -1571,14 +1676,14 @@ pub fn parseAr(self: *Object, macho_file: *MachO) !void {
1571 const cmd = lc.cast(macho.symtab_command).?;1676 const cmd = lc.cast(macho.symtab_command).?;
1572 try self.strtab.resize(gpa, cmd.strsize);1677 try self.strtab.resize(gpa, cmd.strsize);
1573 {1678 {
1574 const amt = try handle.preadAll(self.strtab.items, cmd.stroff + offset);1679 const amt = try handle.preadAll(self.strtab.items, cmd.stroff + self.offset);
1575 if (amt != self.strtab.items.len) return error.InputOutput;1680 if (amt != self.strtab.items.len) return error.InputOutput;
1576 }1681 }
15771682
1578 const symtab_buffer = try gpa.alloc(u8, cmd.nsyms * @sizeOf(macho.nlist_64));1683 const symtab_buffer = try gpa.alloc(u8, cmd.nsyms * @sizeOf(macho.nlist_64));
1579 defer gpa.free(symtab_buffer);1684 defer gpa.free(symtab_buffer);
1580 {1685 {
1581 const amt = try handle.preadAll(symtab_buffer, cmd.symoff + offset);1686 const amt = try handle.preadAll(symtab_buffer, cmd.symoff + self.offset);
1582 if (amt != symtab_buffer.len) return error.InputOutput;1687 if (amt != symtab_buffer.len) return error.InputOutput;
1583 }1688 }
1584 const symtab = @as([*]align(1) const macho.nlist_64, @ptrCast(symtab_buffer.ptr))[0..cmd.nsyms];1689 const symtab = @as([*]align(1) const macho.nlist_64, @ptrCast(symtab_buffer.ptr))[0..cmd.nsyms];
...@@ -1613,7 +1718,7 @@ pub fn updateArSymtab(self: Object, ar_symtab: *Archive.ArSymtab, macho_file: *M...@@ -1613,7 +1718,7 @@ pub fn updateArSymtab(self: Object, ar_symtab: *Archive.ArSymtab, macho_file: *M
1613}1718}
16141719
1615pub fn updateArSize(self: *Object, macho_file: *MachO) !void {1720pub fn updateArSize(self: *Object, macho_file: *MachO) !void {
1616 self.output_ar_state.size = if (self.archive) |ar| ar.size else size: {1721 self.output_ar_state.size = if (self.in_archive) |ar| ar.size else size: {
1617 const file = macho_file.getFileHandle(self.file_handle);1722 const file = macho_file.getFileHandle(self.file_handle);
1618 break :size (try file.stat()).size;1723 break :size (try file.stat()).size;
1619 };1724 };
...@@ -1622,7 +1727,6 @@ pub fn updateArSize(self: *Object, macho_file: *MachO) !void {...@@ -1622,7 +1727,6 @@ pub fn updateArSize(self: *Object, macho_file: *MachO) !void {
1622pub fn writeAr(self: Object, ar_format: Archive.Format, macho_file: *MachO, writer: anytype) !void {1727pub fn writeAr(self: Object, ar_format: Archive.Format, macho_file: *MachO, writer: anytype) !void {
1623 // Header1728 // Header
1624 const size = std.math.cast(usize, self.output_ar_state.size) orelse return error.Overflow;1729 const size = std.math.cast(usize, self.output_ar_state.size) orelse return error.Overflow;
1625 const offset: u64 = if (self.archive) |ar| ar.offset else 0;
1626 try Archive.writeHeader(self.path, size, ar_format, writer);1730 try Archive.writeHeader(self.path, size, ar_format, writer);
1627 // Data1731 // Data
1628 const file = macho_file.getFileHandle(self.file_handle);1732 const file = macho_file.getFileHandle(self.file_handle);
...@@ -1630,72 +1734,75 @@ pub fn writeAr(self: Object, ar_format: Archive.Format, macho_file: *MachO, writ...@@ -1630,72 +1734,75 @@ pub fn writeAr(self: Object, ar_format: Archive.Format, macho_file: *MachO, writ
1630 const gpa = macho_file.base.comp.gpa;1734 const gpa = macho_file.base.comp.gpa;
1631 const data = try gpa.alloc(u8, size);1735 const data = try gpa.alloc(u8, size);
1632 defer gpa.free(data);1736 defer gpa.free(data);
1633 const amt = try file.preadAll(data, offset);1737 const amt = try file.preadAll(data, self.offset);
1634 if (amt != size) return error.InputOutput;1738 if (amt != size) return error.InputOutput;
1635 try writer.writeAll(data);1739 try writer.writeAll(data);
1636}1740}
16371741
1638pub fn calcSymtabSize(self: *Object, macho_file: *MachO) !void {1742pub fn calcSymtabSize(self: *Object, macho_file: *MachO) void {
1639 const tracy = trace(@src());1743 const tracy = trace(@src());
1640 defer tracy.end();1744 defer tracy.end();
16411745
1642 for (self.symbols.items) |sym_index| {1746 const is_obj = macho_file.base.isObject();
1643 const sym = macho_file.getSymbol(sym_index);1747
1644 const file = sym.getFile(macho_file) orelse continue;1748 for (self.symbols.items, 0..) |*sym, i| {
1749 const ref = self.getSymbolRef(@intCast(i), macho_file);
1750 const file = ref.getFile(macho_file) orelse continue;
1645 if (file.getIndex() != self.index) continue;1751 if (file.getIndex() != self.index) continue;
1646 if (sym.getAtom(macho_file)) |atom| if (!atom.flags.alive) continue;1752 if (sym.getAtom(macho_file)) |atom| if (!atom.flags.alive) continue;
1647 if (sym.isSymbolStab(macho_file)) continue;1753 if (sym.isSymbolStab(macho_file)) continue;
1648 const name = sym.getName(macho_file);1754 const name = sym.getName(macho_file);
1755 if (name.len == 0) continue;
1649 // TODO in -r mode, we actually want to merge symbol names and emit only one1756 // TODO in -r mode, we actually want to merge symbol names and emit only one
1650 // work it out when emitting relocs1757 // work it out when emitting relocs
1651 if (name.len > 0 and1758 if ((name[0] == 'L' or name[0] == 'l' or
1652 (name[0] == 'L' or name[0] == 'l' or
1653 mem.startsWith(u8, name, "_OBJC_SELECTOR_REFERENCES_")) and1759 mem.startsWith(u8, name, "_OBJC_SELECTOR_REFERENCES_")) and
1654 !macho_file.base.isObject()) continue;1760 !is_obj)
1761 continue;
1655 sym.flags.output_symtab = true;1762 sym.flags.output_symtab = true;
1656 if (sym.isLocal()) {1763 if (sym.isLocal()) {
1657 try sym.addExtra(.{ .symtab = self.output_symtab_ctx.nlocals }, macho_file);1764 sym.addExtra(.{ .symtab = self.output_symtab_ctx.nlocals }, macho_file);
1658 self.output_symtab_ctx.nlocals += 1;1765 self.output_symtab_ctx.nlocals += 1;
1659 } else if (sym.flags.@"export") {1766 } else if (sym.flags.@"export") {
1660 try sym.addExtra(.{ .symtab = self.output_symtab_ctx.nexports }, macho_file);1767 sym.addExtra(.{ .symtab = self.output_symtab_ctx.nexports }, macho_file);
1661 self.output_symtab_ctx.nexports += 1;1768 self.output_symtab_ctx.nexports += 1;
1662 } else {1769 } else {
1663 assert(sym.flags.import);1770 assert(sym.flags.import);
1664 try sym.addExtra(.{ .symtab = self.output_symtab_ctx.nimports }, macho_file);1771 sym.addExtra(.{ .symtab = self.output_symtab_ctx.nimports }, macho_file);
1665 self.output_symtab_ctx.nimports += 1;1772 self.output_symtab_ctx.nimports += 1;
1666 }1773 }
1667 self.output_symtab_ctx.strsize += @as(u32, @intCast(sym.getName(macho_file).len + 1));1774 self.output_symtab_ctx.strsize += @as(u32, @intCast(sym.getName(macho_file).len + 1));
1668 }1775 }
16691776
1670 if (macho_file.base.comp.config.debug_format != .strip and self.hasDebugInfo())1777 if (macho_file.base.comp.config.debug_format != .strip and self.hasDebugInfo())
1671 try self.calcStabsSize(macho_file);1778 self.calcStabsSize(macho_file);
1672}1779}
16731780
1674pub fn calcStabsSize(self: *Object, macho_file: *MachO) error{Overflow}!void {1781pub fn calcStabsSize(self: *Object, macho_file: *MachO) void {
1675 if (self.compile_unit) |cu| {1782 if (self.compile_unit) |cu| {
1676 const comp_dir = cu.getCompDir(self);1783 const comp_dir = cu.getCompDir(self.*);
1677 const tu_name = cu.getTuName(self);1784 const tu_name = cu.getTuName(self.*);
16781785
1679 self.output_symtab_ctx.nstabs += 4; // N_SO, N_SO, N_OSO, N_SO1786 self.output_symtab_ctx.nstabs += 4; // N_SO, N_SO, N_OSO, N_SO
1680 self.output_symtab_ctx.strsize += @as(u32, @intCast(comp_dir.len + 1)); // comp_dir1787 self.output_symtab_ctx.strsize += @as(u32, @intCast(comp_dir.len + 1)); // comp_dir
1681 self.output_symtab_ctx.strsize += @as(u32, @intCast(tu_name.len + 1)); // tu_name1788 self.output_symtab_ctx.strsize += @as(u32, @intCast(tu_name.len + 1)); // tu_name
16821789
1683 if (self.archive) |ar| {1790 if (self.in_archive) |ar| {
1684 self.output_symtab_ctx.strsize += @as(u32, @intCast(ar.path.len + 1 + self.path.len + 1 + 1));1791 self.output_symtab_ctx.strsize += @as(u32, @intCast(ar.path.len + 1 + self.path.len + 1 + 1));
1685 } else {1792 } else {
1686 self.output_symtab_ctx.strsize += @as(u32, @intCast(self.path.len + 1));1793 self.output_symtab_ctx.strsize += @as(u32, @intCast(self.path.len + 1));
1687 }1794 }
16881795
1689 for (self.symbols.items) |sym_index| {1796 for (self.symbols.items, 0..) |sym, i| {
1690 const sym = macho_file.getSymbol(sym_index);1797 const ref = self.getSymbolRef(@intCast(i), macho_file);
1691 const file = sym.getFile(macho_file) orelse continue;1798 const file = ref.getFile(macho_file) orelse continue;
1692 if (file.getIndex() != self.index) continue;1799 if (file.getIndex() != self.index) continue;
1693 if (!sym.flags.output_symtab) continue;1800 if (!sym.flags.output_symtab) continue;
1694 if (macho_file.base.isObject()) {1801 if (macho_file.base.isObject()) {
1695 const name = sym.getName(macho_file);1802 const name = sym.getName(macho_file);
1696 if (name.len > 0 and (name[0] == 'L' or name[0] == 'l')) continue;1803 if (name.len > 0 and (name[0] == 'L' or name[0] == 'l')) continue;
1697 }1804 }
1698 const sect = macho_file.sections.items(.header)[sym.out_n_sect];1805 const sect = macho_file.sections.items(.header)[sym.getOutputSectionIndex(macho_file)];
1699 if (sect.isCode()) {1806 if (sect.isCode()) {
1700 self.output_symtab_ctx.nstabs += 4; // N_BNSYM, N_FUN, N_FUN, N_ENSYM1807 self.output_symtab_ctx.nstabs += 4; // N_BNSYM, N_FUN, N_FUN, N_ENSYM
1701 } else if (sym.visibility == .global) {1808 } else if (sym.visibility == .global) {
...@@ -1709,12 +1816,12 @@ pub fn calcStabsSize(self: *Object, macho_file: *MachO) error{Overflow}!void {...@@ -1709,12 +1816,12 @@ pub fn calcStabsSize(self: *Object, macho_file: *MachO) error{Overflow}!void {
17091816
1710 for (self.stab_files.items) |sf| {1817 for (self.stab_files.items) |sf| {
1711 self.output_symtab_ctx.nstabs += 4; // N_SO, N_SO, N_OSO, N_SO1818 self.output_symtab_ctx.nstabs += 4; // N_SO, N_SO, N_OSO, N_SO
1712 self.output_symtab_ctx.strsize += @as(u32, @intCast(sf.getCompDir(self).len + 1)); // comp_dir1819 self.output_symtab_ctx.strsize += @as(u32, @intCast(sf.getCompDir(self.*).len + 1)); // comp_dir
1713 self.output_symtab_ctx.strsize += @as(u32, @intCast(sf.getTuName(self).len + 1)); // tu_name1820 self.output_symtab_ctx.strsize += @as(u32, @intCast(sf.getTuName(self.*).len + 1)); // tu_name
1714 self.output_symtab_ctx.strsize += @as(u32, @intCast(sf.getOsoPath(self).len + 1)); // path1821 self.output_symtab_ctx.strsize += @as(u32, @intCast(sf.getOsoPath(self.*).len + 1)); // path
17151822
1716 for (sf.stabs.items) |stab| {1823 for (sf.stabs.items) |stab| {
1717 const sym = stab.getSymbol(macho_file) orelse continue;1824 const sym = stab.getSymbol(self.*) orelse continue;
1718 const file = sym.getFile(macho_file).?;1825 const file = sym.getFile(macho_file).?;
1719 if (file.getIndex() != self.index) continue;1826 if (file.getIndex() != self.index) continue;
1720 if (!sym.flags.output_symtab) continue;1827 if (!sym.flags.output_symtab) continue;
...@@ -1725,28 +1832,212 @@ pub fn calcStabsSize(self: *Object, macho_file: *MachO) error{Overflow}!void {...@@ -1725,28 +1832,212 @@ pub fn calcStabsSize(self: *Object, macho_file: *MachO) error{Overflow}!void {
1725 }1832 }
1726}1833}
17271834
1728pub fn writeSymtab(self: Object, macho_file: *MachO, ctx: anytype) error{Overflow}!void {1835pub fn writeAtoms(self: *Object, macho_file: *MachO) !void {
1729 const tracy = trace(@src());1836 const tracy = trace(@src());
1730 defer tracy.end();1837 defer tracy.end();
17311838
1732 for (self.symbols.items) |sym_index| {1839 const gpa = macho_file.base.comp.gpa;
1733 const sym = macho_file.getSymbol(sym_index);1840 const headers = self.sections.items(.header);
1734 const file = sym.getFile(macho_file) orelse continue;1841 const sections_data = try gpa.alloc([]const u8, headers.len);
1842 defer {
1843 for (sections_data) |data| {
1844 gpa.free(data);
1845 }
1846 gpa.free(sections_data);
1847 }
1848 @memset(sections_data, &[0]u8{});
1849 const file = macho_file.getFileHandle(self.file_handle);
1850
1851 for (headers, 0..) |header, n_sect| {
1852 if (header.isZerofill()) continue;
1853 const size = math.cast(usize, header.size) orelse return error.Overflow;
1854 const data = try gpa.alloc(u8, size);
1855 const amt = try file.preadAll(data, header.offset + self.offset);
1856 if (amt != data.len) return error.InputOutput;
1857 sections_data[n_sect] = data;
1858 }
1859 for (self.getAtoms()) |atom_index| {
1860 const atom = self.getAtom(atom_index) orelse continue;
1861 if (!atom.flags.alive) continue;
1862 const sect = atom.getInputSection(macho_file);
1863 if (sect.isZerofill()) continue;
1864 const value = math.cast(usize, atom.value) orelse return error.Overflow;
1865 const off = math.cast(usize, atom.off) orelse return error.Overflow;
1866 const size = math.cast(usize, atom.size) orelse return error.Overflow;
1867 const buffer = macho_file.sections.items(.out)[atom.out_n_sect].items;
1868 const data = sections_data[atom.n_sect];
1869 @memcpy(buffer[value..][0..size], data[off..][0..size]);
1870 try atom.resolveRelocs(macho_file, buffer[value..][0..size]);
1871 }
1872}
1873
1874pub fn writeAtomsRelocatable(self: *Object, macho_file: *MachO) !void {
1875 const tracy = trace(@src());
1876 defer tracy.end();
1877
1878 const gpa = macho_file.base.comp.gpa;
1879 const headers = self.sections.items(.header);
1880 const sections_data = try gpa.alloc([]const u8, headers.len);
1881 defer {
1882 for (sections_data) |data| {
1883 gpa.free(data);
1884 }
1885 gpa.free(sections_data);
1886 }
1887 @memset(sections_data, &[0]u8{});
1888 const file = macho_file.getFileHandle(self.file_handle);
1889
1890 for (headers, 0..) |header, n_sect| {
1891 if (header.isZerofill()) continue;
1892 const size = math.cast(usize, header.size) orelse return error.Overflow;
1893 const data = try gpa.alloc(u8, size);
1894 const amt = try file.preadAll(data, header.offset + self.offset);
1895 if (amt != data.len) return error.InputOutput;
1896 sections_data[n_sect] = data;
1897 }
1898 for (self.getAtoms()) |atom_index| {
1899 const atom = self.getAtom(atom_index) orelse continue;
1900 if (!atom.flags.alive) continue;
1901 const sect = atom.getInputSection(macho_file);
1902 if (sect.isZerofill()) continue;
1903 const value = math.cast(usize, atom.value) orelse return error.Overflow;
1904 const off = math.cast(usize, atom.off) orelse return error.Overflow;
1905 const size = math.cast(usize, atom.size) orelse return error.Overflow;
1906 const buffer = macho_file.sections.items(.out)[atom.out_n_sect].items;
1907 const data = sections_data[atom.n_sect];
1908 @memcpy(buffer[value..][0..size], data[off..][0..size]);
1909 const relocs = macho_file.sections.items(.relocs)[atom.out_n_sect].items;
1910 const extra = atom.getExtra(macho_file);
1911 try atom.writeRelocs(macho_file, buffer[value..][0..size], relocs[extra.rel_out_index..][0..extra.rel_out_count]);
1912 }
1913}
1914
1915pub fn calcCompactUnwindSizeRelocatable(self: *Object, macho_file: *MachO) void {
1916 const tracy = trace(@src());
1917 defer tracy.end();
1918
1919 const ctx = &self.compact_unwind_ctx;
1920
1921 for (self.unwind_records_indexes.items) |irec| {
1922 const rec = self.getUnwindRecord(irec);
1923 if (!rec.alive) continue;
1924
1925 ctx.rec_count += 1;
1926 ctx.reloc_count += 1;
1927 if (rec.getPersonality(macho_file)) |_| {
1928 ctx.reloc_count += 1;
1929 }
1930 if (rec.getLsdaAtom(macho_file)) |_| {
1931 ctx.reloc_count += 1;
1932 }
1933 }
1934}
1935
1936pub fn writeCompactUnwindRelocatable(self: *Object, macho_file: *MachO) !void {
1937 const tracy = trace(@src());
1938 defer tracy.end();
1939
1940 const cpu_arch = macho_file.getTarget().cpu.arch;
1941
1942 const addReloc = struct {
1943 fn addReloc(offset: u32, arch: std.Target.Cpu.Arch) !macho.relocation_info {
1944 return .{
1945 .r_address = math.cast(i32, offset) orelse return error.Overflow,
1946 .r_symbolnum = 0,
1947 .r_pcrel = 0,
1948 .r_length = 3,
1949 .r_extern = 0,
1950 .r_type = switch (arch) {
1951 .aarch64 => @intFromEnum(macho.reloc_type_arm64.ARM64_RELOC_UNSIGNED),
1952 .x86_64 => @intFromEnum(macho.reloc_type_x86_64.X86_64_RELOC_UNSIGNED),
1953 else => unreachable,
1954 },
1955 };
1956 }
1957 }.addReloc;
1958
1959 const nsect = macho_file.unwind_info_sect_index.?;
1960 const buffer = macho_file.sections.items(.out)[nsect].items;
1961 const relocs = macho_file.sections.items(.relocs)[nsect].items;
1962
1963 var rec_index: u32 = self.compact_unwind_ctx.rec_index;
1964 var reloc_index: u32 = self.compact_unwind_ctx.reloc_index;
1965
1966 for (self.unwind_records_indexes.items) |irec| {
1967 const rec = self.getUnwindRecord(irec);
1968 if (!rec.alive) continue;
1969
1970 var out: macho.compact_unwind_entry = .{
1971 .rangeStart = 0,
1972 .rangeLength = rec.length,
1973 .compactUnwindEncoding = rec.enc.enc,
1974 .personalityFunction = 0,
1975 .lsda = 0,
1976 };
1977 defer rec_index += 1;
1978
1979 const offset = rec_index * @sizeOf(macho.compact_unwind_entry);
1980
1981 {
1982 // Function address
1983 const atom = rec.getAtom(macho_file);
1984 const addr = rec.getAtomAddress(macho_file);
1985 out.rangeStart = addr;
1986 var reloc = try addReloc(offset, cpu_arch);
1987 reloc.r_symbolnum = atom.out_n_sect + 1;
1988 relocs[reloc_index] = reloc;
1989 reloc_index += 1;
1990 }
1991
1992 // Personality function
1993 if (rec.getPersonality(macho_file)) |sym| {
1994 const r_symbolnum = math.cast(u24, sym.getOutputSymtabIndex(macho_file).?) orelse return error.Overflow;
1995 var reloc = try addReloc(offset + 16, cpu_arch);
1996 reloc.r_symbolnum = r_symbolnum;
1997 reloc.r_extern = 1;
1998 relocs[reloc_index] = reloc;
1999 reloc_index += 1;
2000 }
2001
2002 // LSDA address
2003 if (rec.getLsdaAtom(macho_file)) |atom| {
2004 const addr = rec.getLsdaAddress(macho_file);
2005 out.lsda = addr;
2006 var reloc = try addReloc(offset + 24, cpu_arch);
2007 reloc.r_symbolnum = atom.out_n_sect + 1;
2008 relocs[reloc_index] = reloc;
2009 reloc_index += 1;
2010 }
2011
2012 @memcpy(buffer[offset..][0..@sizeOf(macho.compact_unwind_entry)], mem.asBytes(&out));
2013 }
2014}
2015
2016pub fn writeSymtab(self: Object, macho_file: *MachO, ctx: anytype) void {
2017 const tracy = trace(@src());
2018 defer tracy.end();
2019
2020 var n_strx = self.output_symtab_ctx.stroff;
2021 for (self.symbols.items, 0..) |sym, i| {
2022 const ref = self.getSymbolRef(@intCast(i), macho_file);
2023 const file = ref.getFile(macho_file) orelse continue;
1735 if (file.getIndex() != self.index) continue;2024 if (file.getIndex() != self.index) continue;
1736 const idx = sym.getOutputSymtabIndex(macho_file) orelse continue;2025 const idx = sym.getOutputSymtabIndex(macho_file) orelse continue;
1737 const n_strx = @as(u32, @intCast(ctx.strtab.items.len));
1738 ctx.strtab.appendSliceAssumeCapacity(sym.getName(macho_file));
1739 ctx.strtab.appendAssumeCapacity(0);
1740 const out_sym = &ctx.symtab.items[idx];2026 const out_sym = &ctx.symtab.items[idx];
1741 out_sym.n_strx = n_strx;2027 out_sym.n_strx = n_strx;
1742 sym.setOutputSym(macho_file, out_sym);2028 sym.setOutputSym(macho_file, out_sym);
2029 const name = sym.getName(macho_file);
2030 @memcpy(ctx.strtab.items[n_strx..][0..name.len], name);
2031 n_strx += @intCast(name.len);
2032 ctx.strtab.items[n_strx] = 0;
2033 n_strx += 1;
1743 }2034 }
17442035
1745 if (macho_file.base.comp.config.debug_format != .strip and self.hasDebugInfo())2036 if (macho_file.base.comp.config.debug_format != .strip and self.hasDebugInfo())
1746 try self.writeStabs(macho_file, ctx);2037 self.writeStabs(n_strx, macho_file, ctx);
1747}2038}
17482039
1749pub fn writeStabs(self: *const Object, macho_file: *MachO, ctx: anytype) error{Overflow}!void {2040pub fn writeStabs(self: Object, stroff: u32, macho_file: *MachO, ctx: anytype) void {
1750 const writeFuncStab = struct {2041 const writeFuncStab = struct {
1751 inline fn writeFuncStab(2042 inline fn writeFuncStab(
1752 n_strx: u32,2043 n_strx: u32,
...@@ -1788,6 +2079,7 @@ pub fn writeStabs(self: *const Object, macho_file: *MachO, ctx: anytype) error{O...@@ -1788,6 +2079,7 @@ pub fn writeStabs(self: *const Object, macho_file: *MachO, ctx: anytype) error{O
1788 }.writeFuncStab;2079 }.writeFuncStab;
17892080
1790 var index = self.output_symtab_ctx.istab;2081 var index = self.output_symtab_ctx.istab;
2082 var n_strx = stroff;
17912083
1792 if (self.compile_unit) |cu| {2084 if (self.compile_unit) |cu| {
1793 const comp_dir = cu.getCompDir(self);2085 const comp_dir = cu.getCompDir(self);
...@@ -1795,9 +2087,6 @@ pub fn writeStabs(self: *const Object, macho_file: *MachO, ctx: anytype) error{O...@@ -1795,9 +2087,6 @@ pub fn writeStabs(self: *const Object, macho_file: *MachO, ctx: anytype) error{O
17952087
1796 // Open scope2088 // Open scope
1797 // N_SO comp_dir2089 // N_SO comp_dir
1798 var n_strx = @as(u32, @intCast(ctx.strtab.items.len));
1799 ctx.strtab.appendSliceAssumeCapacity(comp_dir);
1800 ctx.strtab.appendAssumeCapacity(0);
1801 ctx.symtab.items[index] = .{2090 ctx.symtab.items[index] = .{
1802 .n_strx = n_strx,2091 .n_strx = n_strx,
1803 .n_type = macho.N_SO,2092 .n_type = macho.N_SO,
...@@ -1806,11 +2095,12 @@ pub fn writeStabs(self: *const Object, macho_file: *MachO, ctx: anytype) error{O...@@ -1806,11 +2095,12 @@ pub fn writeStabs(self: *const Object, macho_file: *MachO, ctx: anytype) error{O
1806 .n_value = 0,2095 .n_value = 0,
1807 };2096 };
1808 index += 1;2097 index += 1;
2098 @memcpy(ctx.strtab.items[n_strx..][0..comp_dir.len], comp_dir);
2099 n_strx += @intCast(comp_dir.len);
2100 ctx.strtab.items[n_strx] = 0;
2101 n_strx += 1;
1809 // N_SO tu_name2102 // N_SO tu_name
1810 n_strx = @as(u32, @intCast(ctx.strtab.items.len));2103 macho_file.symtab.items[index] = .{
1811 ctx.strtab.appendSliceAssumeCapacity(tu_name);
1812 ctx.strtab.appendAssumeCapacity(0);
1813 ctx.symtab.items[index] = .{
1814 .n_strx = n_strx,2104 .n_strx = n_strx,
1815 .n_type = macho.N_SO,2105 .n_type = macho.N_SO,
1816 .n_sect = 0,2106 .n_sect = 0,
...@@ -1818,18 +2108,11 @@ pub fn writeStabs(self: *const Object, macho_file: *MachO, ctx: anytype) error{O...@@ -1818,18 +2108,11 @@ pub fn writeStabs(self: *const Object, macho_file: *MachO, ctx: anytype) error{O
1818 .n_value = 0,2108 .n_value = 0,
1819 };2109 };
1820 index += 1;2110 index += 1;
2111 @memcpy(ctx.strtab.items[n_strx..][0..tu_name.len], tu_name);
2112 n_strx += @intCast(tu_name.len);
2113 ctx.strtab.items[n_strx] = 0;
2114 n_strx += 1;
1821 // N_OSO path2115 // N_OSO path
1822 n_strx = @as(u32, @intCast(ctx.strtab.items.len));
1823 if (self.archive) |ar| {
1824 ctx.strtab.appendSliceAssumeCapacity(ar.path);
1825 ctx.strtab.appendAssumeCapacity('(');
1826 ctx.strtab.appendSliceAssumeCapacity(self.path);
1827 ctx.strtab.appendAssumeCapacity(')');
1828 ctx.strtab.appendAssumeCapacity(0);
1829 } else {
1830 ctx.strtab.appendSliceAssumeCapacity(self.path);
1831 ctx.strtab.appendAssumeCapacity(0);
1832 }
1833 ctx.symtab.items[index] = .{2116 ctx.symtab.items[index] = .{
1834 .n_strx = n_strx,2117 .n_strx = n_strx,
1835 .n_type = macho.N_OSO,2118 .n_type = macho.N_OSO,
...@@ -1838,23 +2121,40 @@ pub fn writeStabs(self: *const Object, macho_file: *MachO, ctx: anytype) error{O...@@ -1838,23 +2121,40 @@ pub fn writeStabs(self: *const Object, macho_file: *MachO, ctx: anytype) error{O
1838 .n_value = self.mtime,2121 .n_value = self.mtime,
1839 };2122 };
1840 index += 1;2123 index += 1;
2124 if (self.in_archive) |ar| {
2125 @memcpy(ctx.strtab.items[n_strx..][0..ar.path.len], ar.path);
2126 n_strx += @intCast(ar.path.len);
2127 ctx.strtab.items[n_strx] = '(';
2128 n_strx += 1;
2129 @memcpy(ctx.strtab.items[n_strx..][0..self.path.len], self.path);
2130 n_strx += @intCast(self.path.len);
2131 ctx.strtab.items[n_strx] = ')';
2132 n_strx += 1;
2133 ctx.strtab.items[n_strx] = 0;
2134 n_strx += 1;
2135 } else {
2136 @memcpy(ctx.strtab.items[n_strx..][0..self.path.len], self.path);
2137 n_strx += @intCast(self.path.len);
2138 ctx.strtab.items[n_strx] = 0;
2139 n_strx += 1;
2140 }
18412141
1842 for (self.symbols.items) |sym_index| {2142 for (self.symbols.items, 0..) |sym, i| {
1843 const sym = macho_file.getSymbol(sym_index);2143 const ref = self.getSymbolRef(@intCast(i), macho_file);
1844 const file = sym.getFile(macho_file) orelse continue;2144 const file = ref.getFile(macho_file) orelse continue;
1845 if (file.getIndex() != self.index) continue;2145 if (file.getIndex() != self.index) continue;
1846 if (!sym.flags.output_symtab) continue;2146 if (!sym.flags.output_symtab) continue;
1847 if (macho_file.base.isObject()) {2147 if (macho_file.base.isObject()) {
1848 const name = sym.getName(macho_file);2148 const name = sym.getName(macho_file);
1849 if (name.len > 0 and (name[0] == 'L' or name[0] == 'l')) continue;2149 if (name.len > 0 and (name[0] == 'L' or name[0] == 'l')) continue;
1850 }2150 }
1851 const sect = macho_file.sections.items(.header)[sym.out_n_sect];2151 const sect = macho_file.sections.items(.header)[sym.getOutputSectionIndex(macho_file)];
1852 const sym_n_strx = n_strx: {2152 const sym_n_strx = n_strx: {
1853 const symtab_index = sym.getOutputSymtabIndex(macho_file).?;2153 const symtab_index = sym.getOutputSymtabIndex(macho_file).?;
1854 const osym = ctx.symtab.items[symtab_index];2154 const osym = ctx.symtab.items[symtab_index];
1855 break :n_strx osym.n_strx;2155 break :n_strx osym.n_strx;
1856 };2156 };
1857 const sym_n_sect: u8 = if (!sym.flags.abs) @intCast(sym.out_n_sect + 1) else 0;2157 const sym_n_sect: u8 = if (!sym.flags.abs) @intCast(sym.getOutputSectionIndex(macho_file) + 1) else 0;
1858 const sym_n_value = sym.getAddress(.{}, macho_file);2158 const sym_n_value = sym.getAddress(.{}, macho_file);
1859 const sym_size = sym.getSize(macho_file);2159 const sym_size = sym.getSize(macho_file);
1860 if (sect.isCode()) {2160 if (sect.isCode()) {
...@@ -1894,11 +2194,12 @@ pub fn writeStabs(self: *const Object, macho_file: *MachO, ctx: anytype) error{O...@@ -1894,11 +2194,12 @@ pub fn writeStabs(self: *const Object, macho_file: *MachO, ctx: anytype) error{O
1894 assert(self.hasSymbolStabs());2194 assert(self.hasSymbolStabs());
18952195
1896 for (self.stab_files.items) |sf| {2196 for (self.stab_files.items) |sf| {
2197 const comp_dir = sf.getCompDir(self);
2198 const tu_name = sf.getTuName(self);
2199 const oso_path = sf.getOsoPath(self);
2200
1897 // Open scope2201 // Open scope
1898 // N_SO comp_dir2202 // N_SO comp_dir
1899 var n_strx = @as(u32, @intCast(ctx.strtab.items.len));
1900 ctx.strtab.appendSliceAssumeCapacity(sf.getCompDir(self));
1901 ctx.strtab.appendAssumeCapacity(0);
1902 ctx.symtab.items[index] = .{2203 ctx.symtab.items[index] = .{
1903 .n_strx = n_strx,2204 .n_strx = n_strx,
1904 .n_type = macho.N_SO,2205 .n_type = macho.N_SO,
...@@ -1907,10 +2208,11 @@ pub fn writeStabs(self: *const Object, macho_file: *MachO, ctx: anytype) error{O...@@ -1907,10 +2208,11 @@ pub fn writeStabs(self: *const Object, macho_file: *MachO, ctx: anytype) error{O
1907 .n_value = 0,2208 .n_value = 0,
1908 };2209 };
1909 index += 1;2210 index += 1;
2211 @memcpy(ctx.strtab.items[n_strx..][0..comp_dir.len], comp_dir);
2212 n_strx += @intCast(comp_dir.len);
2213 ctx.strtab.items[n_strx] = 0;
2214 n_strx += 1;
1910 // N_SO tu_name2215 // N_SO tu_name
1911 n_strx = @as(u32, @intCast(ctx.strtab.items.len));
1912 ctx.strtab.appendSliceAssumeCapacity(sf.getTuName(self));
1913 ctx.strtab.appendAssumeCapacity(0);
1914 ctx.symtab.items[index] = .{2216 ctx.symtab.items[index] = .{
1915 .n_strx = n_strx,2217 .n_strx = n_strx,
1916 .n_type = macho.N_SO,2218 .n_type = macho.N_SO,
...@@ -1919,10 +2221,11 @@ pub fn writeStabs(self: *const Object, macho_file: *MachO, ctx: anytype) error{O...@@ -1919,10 +2221,11 @@ pub fn writeStabs(self: *const Object, macho_file: *MachO, ctx: anytype) error{O
1919 .n_value = 0,2221 .n_value = 0,
1920 };2222 };
1921 index += 1;2223 index += 1;
2224 @memcpy(ctx.strtab.items[n_strx..][0..tu_name.len], tu_name);
2225 n_strx += @intCast(tu_name.len);
2226 ctx.strtab.items[n_strx] = 0;
2227 n_strx += 1;
1922 // N_OSO path2228 // N_OSO path
1923 n_strx = @as(u32, @intCast(ctx.strtab.items.len));
1924 ctx.strtab.appendSliceAssumeCapacity(sf.getOsoPath(self));
1925 ctx.strtab.appendAssumeCapacity(0);
1926 ctx.symtab.items[index] = .{2229 ctx.symtab.items[index] = .{
1927 .n_strx = n_strx,2230 .n_strx = n_strx,
1928 .n_type = macho.N_OSO,2231 .n_type = macho.N_OSO,
...@@ -1931,9 +2234,13 @@ pub fn writeStabs(self: *const Object, macho_file: *MachO, ctx: anytype) error{O...@@ -1931,9 +2234,13 @@ pub fn writeStabs(self: *const Object, macho_file: *MachO, ctx: anytype) error{O
1931 .n_value = sf.getOsoModTime(self),2234 .n_value = sf.getOsoModTime(self),
1932 };2235 };
1933 index += 1;2236 index += 1;
2237 @memcpy(ctx.strtab.items[n_strx..][0..oso_path.len], oso_path);
2238 n_strx += @intCast(oso_path.len);
2239 ctx.strtab.items[n_strx] = 0;
2240 n_strx += 1;
19342241
1935 for (sf.stabs.items) |stab| {2242 for (sf.stabs.items) |stab| {
1936 const sym = stab.getSymbol(macho_file) orelse continue;2243 const sym = stab.getSymbol(self) orelse continue;
1937 const file = sym.getFile(macho_file).?;2244 const file = sym.getFile(macho_file).?;
1938 if (file.getIndex() != self.index) continue;2245 if (file.getIndex() != self.index) continue;
1939 if (!sym.flags.output_symtab) continue;2246 if (!sym.flags.output_symtab) continue;
...@@ -1942,7 +2249,7 @@ pub fn writeStabs(self: *const Object, macho_file: *MachO, ctx: anytype) error{O...@@ -1942,7 +2249,7 @@ pub fn writeStabs(self: *const Object, macho_file: *MachO, ctx: anytype) error{O
1942 const osym = ctx.symtab.items[symtab_index];2249 const osym = ctx.symtab.items[symtab_index];
1943 break :n_strx osym.n_strx;2250 break :n_strx osym.n_strx;
1944 };2251 };
1945 const sym_n_sect: u8 = if (!sym.flags.abs) @intCast(sym.out_n_sect + 1) else 0;2252 const sym_n_sect: u8 = if (!sym.flags.abs) @intCast(sym.getOutputSectionIndex(macho_file) + 1) else 0;
1946 const sym_n_value = sym.getAddress(.{}, macho_file);2253 const sym_n_value = sym.getAddress(.{}, macho_file);
1947 const sym_size = sym.getSize(macho_file);2254 const sym_size = sym.getSize(macho_file);
1948 if (stab.is_func) {2255 if (stab.is_func) {
...@@ -1983,34 +2290,8 @@ pub fn writeStabs(self: *const Object, macho_file: *MachO, ctx: anytype) error{O...@@ -1983,34 +2290,8 @@ pub fn writeStabs(self: *const Object, macho_file: *MachO, ctx: anytype) error{O
1983 }2290 }
1984}2291}
19852292
1986fn getSectionData(self: *const Object, index: u32, macho_file: *MachO) ![]u8 {
1987 const gpa = macho_file.base.comp.gpa;
1988 const slice = self.sections.slice();
1989 assert(index < slice.items(.header).len);
1990 const sect = slice.items(.header)[index];
1991 const handle = macho_file.getFileHandle(self.file_handle);
1992 const offset = if (self.archive) |ar| ar.offset else 0;
1993 const size = math.cast(usize, sect.size) orelse return error.Overflow;
1994 const buffer = try gpa.alloc(u8, size);
1995 errdefer gpa.free(buffer);
1996 const amt = try handle.preadAll(buffer, sect.offset + offset);
1997 if (amt != buffer.len) return error.InputOutput;
1998 return buffer;
1999}
2000
2001pub fn getAtomData(self: *const Object, macho_file: *MachO, atom: Atom, buffer: []u8) !void {
2002 assert(buffer.len == atom.size);
2003 const slice = self.sections.slice();
2004 const handle = macho_file.getFileHandle(self.file_handle);
2005 const offset = if (self.archive) |ar| ar.offset else 0;
2006 const sect = slice.items(.header)[atom.n_sect];
2007 const amt = try handle.preadAll(buffer, sect.offset + offset + atom.off);
2008 if (amt != buffer.len) return error.InputOutput;
2009}
2010
2011pub fn getAtomRelocs(self: *const Object, atom: Atom, macho_file: *MachO) []const Relocation {2293pub fn getAtomRelocs(self: *const Object, atom: Atom, macho_file: *MachO) []const Relocation {
2012 if (!atom.flags.relocs) return &[0]Relocation{};2294 const extra = atom.getExtra(macho_file);
2013 const extra = atom.getExtra(macho_file).?;
2014 const relocs = self.sections.items(.relocs)[atom.n_sect];2295 const relocs = self.sections.items(.relocs)[atom.n_sect];
2015 return relocs.items[extra.rel_index..][0..extra.rel_count];2296 return relocs.items[extra.rel_index..][0..extra.rel_count];
2016}2297}
...@@ -2068,6 +2349,160 @@ pub fn asFile(self: *Object) File {...@@ -2068,6 +2349,160 @@ pub fn asFile(self: *Object) File {
2068 return .{ .object = self };2349 return .{ .object = self };
2069}2350}
20702351
2352const AddAtomArgs = struct {
2353 name: u32,
2354 n_sect: u8,
2355 off: u64,
2356 size: u64,
2357 alignment: u32,
2358};
2359
2360fn addAtom(self: *Object, allocator: Allocator, args: AddAtomArgs) !Atom.Index {
2361 const atom_index: Atom.Index = @intCast(self.atoms.items.len);
2362 const atom = try self.atoms.addOne(allocator);
2363 atom.* = .{
2364 .file = self.index,
2365 .atom_index = atom_index,
2366 .name = args.name,
2367 .n_sect = args.n_sect,
2368 .size = args.size,
2369 .off = args.off,
2370 .extra = try self.addAtomExtra(allocator, .{}),
2371 .alignment = Atom.Alignment.fromLog2Units(args.alignment),
2372 };
2373 return atom_index;
2374}
2375
2376pub fn getAtom(self: *Object, atom_index: Atom.Index) ?*Atom {
2377 if (atom_index == 0) return null;
2378 assert(atom_index < self.atoms.items.len);
2379 return &self.atoms.items[atom_index];
2380}
2381
2382pub fn getAtoms(self: *Object) []const Atom.Index {
2383 return self.atoms_indexes.items;
2384}
2385
2386fn addAtomExtra(self: *Object, allocator: Allocator, extra: Atom.Extra) !u32 {
2387 const fields = @typeInfo(Atom.Extra).Struct.fields;
2388 try self.atoms_extra.ensureUnusedCapacity(allocator, fields.len);
2389 return self.addAtomExtraAssumeCapacity(extra);
2390}
2391
2392fn addAtomExtraAssumeCapacity(self: *Object, extra: Atom.Extra) u32 {
2393 const index = @as(u32, @intCast(self.atoms_extra.items.len));
2394 const fields = @typeInfo(Atom.Extra).Struct.fields;
2395 inline for (fields) |field| {
2396 self.atoms_extra.appendAssumeCapacity(switch (field.type) {
2397 u32 => @field(extra, field.name),
2398 else => @compileError("bad field type"),
2399 });
2400 }
2401 return index;
2402}
2403
2404pub fn getAtomExtra(self: Object, index: u32) Atom.Extra {
2405 const fields = @typeInfo(Atom.Extra).Struct.fields;
2406 var i: usize = index;
2407 var result: Atom.Extra = undefined;
2408 inline for (fields) |field| {
2409 @field(result, field.name) = switch (field.type) {
2410 u32 => self.atoms_extra.items[i],
2411 else => @compileError("bad field type"),
2412 };
2413 i += 1;
2414 }
2415 return result;
2416}
2417
2418pub fn setAtomExtra(self: *Object, index: u32, extra: Atom.Extra) void {
2419 assert(index > 0);
2420 const fields = @typeInfo(Atom.Extra).Struct.fields;
2421 inline for (fields, 0..) |field, i| {
2422 self.atoms_extra.items[index + i] = switch (field.type) {
2423 u32 => @field(extra, field.name),
2424 else => @compileError("bad field type"),
2425 };
2426 }
2427}
2428
2429fn addSymbol(self: *Object, allocator: Allocator) !Symbol.Index {
2430 try self.symbols.ensureUnusedCapacity(allocator, 1);
2431 return self.addSymbolAssumeCapacity();
2432}
2433
2434fn addSymbolAssumeCapacity(self: *Object) Symbol.Index {
2435 const index: Symbol.Index = @intCast(self.symbols.items.len);
2436 const symbol = self.symbols.addOneAssumeCapacity();
2437 symbol.* = .{ .file = self.index };
2438 return index;
2439}
2440
2441pub fn getSymbolRef(self: Object, index: Symbol.Index, macho_file: *MachO) MachO.Ref {
2442 const global_index = self.globals.items[index];
2443 if (macho_file.resolver.get(global_index)) |ref| return ref;
2444 return .{ .index = index, .file = self.index };
2445}
2446
2447pub fn addSymbolExtra(self: *Object, allocator: Allocator, extra: Symbol.Extra) !u32 {
2448 const fields = @typeInfo(Symbol.Extra).Struct.fields;
2449 try self.symbols_extra.ensureUnusedCapacity(allocator, fields.len);
2450 return self.addSymbolExtraAssumeCapacity(extra);
2451}
2452
2453fn addSymbolExtraAssumeCapacity(self: *Object, extra: Symbol.Extra) u32 {
2454 const index = @as(u32, @intCast(self.symbols_extra.items.len));
2455 const fields = @typeInfo(Symbol.Extra).Struct.fields;
2456 inline for (fields) |field| {
2457 self.symbols_extra.appendAssumeCapacity(switch (field.type) {
2458 u32 => @field(extra, field.name),
2459 else => @compileError("bad field type"),
2460 });
2461 }
2462 return index;
2463}
2464
2465pub fn getSymbolExtra(self: Object, index: u32) Symbol.Extra {
2466 const fields = @typeInfo(Symbol.Extra).Struct.fields;
2467 var i: usize = index;
2468 var result: Symbol.Extra = undefined;
2469 inline for (fields) |field| {
2470 @field(result, field.name) = switch (field.type) {
2471 u32 => self.symbols_extra.items[i],
2472 else => @compileError("bad field type"),
2473 };
2474 i += 1;
2475 }
2476 return result;
2477}
2478
2479pub fn setSymbolExtra(self: *Object, index: u32, extra: Symbol.Extra) void {
2480 const fields = @typeInfo(Symbol.Extra).Struct.fields;
2481 inline for (fields, 0..) |field, i| {
2482 self.symbols_extra.items[index + i] = switch (field.type) {
2483 u32 => @field(extra, field.name),
2484 else => @compileError("bad field type"),
2485 };
2486 }
2487}
2488
2489fn addUnwindRecord(self: *Object, allocator: Allocator) !UnwindInfo.Record.Index {
2490 try self.unwind_records.ensureUnusedCapacity(allocator, 1);
2491 return self.addUnwindRecordAssumeCapacity();
2492}
2493
2494fn addUnwindRecordAssumeCapacity(self: *Object) UnwindInfo.Record.Index {
2495 const index = @as(UnwindInfo.Record.Index, @intCast(self.unwind_records.items.len));
2496 const rec = self.unwind_records.addOneAssumeCapacity();
2497 rec.* = .{ .file = self.index };
2498 return index;
2499}
2500
2501pub fn getUnwindRecord(self: *Object, index: UnwindInfo.Record.Index) *UnwindInfo.Record {
2502 assert(index < self.unwind_records.items.len);
2503 return &self.unwind_records.items[index];
2504}
2505
2071pub fn format(2506pub fn format(
2072 self: *Object,2507 self: *Object,
2073 comptime unused_fmt_string: []const u8,2508 comptime unused_fmt_string: []const u8,
...@@ -2102,10 +2537,11 @@ fn formatAtoms(...@@ -2102,10 +2537,11 @@ fn formatAtoms(
2102 _ = unused_fmt_string;2537 _ = unused_fmt_string;
2103 _ = options;2538 _ = options;
2104 const object = ctx.object;2539 const object = ctx.object;
2540 const macho_file = ctx.macho_file;
2105 try writer.writeAll(" atoms\n");2541 try writer.writeAll(" atoms\n");
2106 for (object.atoms.items) |atom_index| {2542 for (object.getAtoms()) |atom_index| {
2107 const atom = ctx.macho_file.getAtom(atom_index).?;2543 const atom = object.getAtom(atom_index) orelse continue;
2108 try writer.print(" {}\n", .{atom.fmt(ctx.macho_file)});2544 try writer.print(" {}\n", .{atom.fmt(macho_file)});
2109 }2545 }
2110}2546}
21112547
...@@ -2171,8 +2607,8 @@ fn formatUnwindRecords(...@@ -2171,8 +2607,8 @@ fn formatUnwindRecords(
2171 const object = ctx.object;2607 const object = ctx.object;
2172 const macho_file = ctx.macho_file;2608 const macho_file = ctx.macho_file;
2173 try writer.writeAll(" unwind records\n");2609 try writer.writeAll(" unwind records\n");
2174 for (object.unwind_records.items) |rec| {2610 for (object.unwind_records_indexes.items) |rec| {
2175 try writer.print(" rec({d}) : {}\n", .{ rec, macho_file.getUnwindRecord(rec).fmt(macho_file) });2611 try writer.print(" rec({d}) : {}\n", .{ rec, object.getUnwindRecord(rec).fmt(macho_file) });
2176 }2612 }
2177}2613}
21782614
...@@ -2192,10 +2628,26 @@ fn formatSymtab(...@@ -2192,10 +2628,26 @@ fn formatSymtab(
2192 _ = unused_fmt_string;2628 _ = unused_fmt_string;
2193 _ = options;2629 _ = options;
2194 const object = ctx.object;2630 const object = ctx.object;
2631 const macho_file = ctx.macho_file;
2195 try writer.writeAll(" symbols\n");2632 try writer.writeAll(" symbols\n");
2196 for (object.symbols.items) |index| {2633 for (object.symbols.items, 0..) |sym, i| {
2197 const sym = ctx.macho_file.getSymbol(index);2634 const ref = object.getSymbolRef(@intCast(i), macho_file);
2198 try writer.print(" {}\n", .{sym.fmt(ctx.macho_file)});2635 if (ref.getFile(macho_file) == null) {
2636 // TODO any better way of handling this?
2637 try writer.print(" {s} : unclaimed\n", .{sym.getName(macho_file)});
2638 } else {
2639 try writer.print(" {}\n", .{ref.getSymbol(macho_file).?.fmt(macho_file)});
2640 }
2641 }
2642 for (object.stab_files.items) |sf| {
2643 try writer.print(" stabs({s},{s},{s})\n", .{
2644 sf.getCompDir(object.*),
2645 sf.getTuName(object.*),
2646 sf.getOsoPath(object.*),
2647 });
2648 for (sf.stabs.items) |stab| {
2649 try writer.print(" {}", .{stab.fmt(object.*)});
2650 }
2199 }2651 }
2200}2652}
22012653
...@@ -2211,7 +2663,7 @@ fn formatPath(...@@ -2211,7 +2663,7 @@ fn formatPath(
2211) !void {2663) !void {
2212 _ = unused_fmt_string;2664 _ = unused_fmt_string;
2213 _ = options;2665 _ = options;
2214 if (object.archive) |ar| {2666 if (object.in_archive) |ar| {
2215 try writer.writeAll(ar.path);2667 try writer.writeAll(ar.path);
2216 try writer.writeByte('(');2668 try writer.writeByte('(');
2217 try writer.writeAll(object.path);2669 try writer.writeAll(object.path);
...@@ -2240,32 +2692,71 @@ const StabFile = struct {...@@ -2240,32 +2692,71 @@ const StabFile = struct {
2240 comp_dir: u32,2692 comp_dir: u32,
2241 stabs: std.ArrayListUnmanaged(Stab) = .{},2693 stabs: std.ArrayListUnmanaged(Stab) = .{},
22422694
2243 fn getCompDir(sf: StabFile, object: *const Object) [:0]const u8 {2695 fn getCompDir(sf: StabFile, object: Object) [:0]const u8 {
2244 const nlist = object.symtab.items(.nlist)[sf.comp_dir];2696 const nlist = object.symtab.items(.nlist)[sf.comp_dir];
2245 return object.getString(nlist.n_strx);2697 return object.getString(nlist.n_strx);
2246 }2698 }
22472699
2248 fn getTuName(sf: StabFile, object: *const Object) [:0]const u8 {2700 fn getTuName(sf: StabFile, object: Object) [:0]const u8 {
2249 const nlist = object.symtab.items(.nlist)[sf.comp_dir + 1];2701 const nlist = object.symtab.items(.nlist)[sf.comp_dir + 1];
2250 return object.getString(nlist.n_strx);2702 return object.getString(nlist.n_strx);
2251 }2703 }
22522704
2253 fn getOsoPath(sf: StabFile, object: *const Object) [:0]const u8 {2705 fn getOsoPath(sf: StabFile, object: Object) [:0]const u8 {
2254 const nlist = object.symtab.items(.nlist)[sf.comp_dir + 2];2706 const nlist = object.symtab.items(.nlist)[sf.comp_dir + 2];
2255 return object.getString(nlist.n_strx);2707 return object.getString(nlist.n_strx);
2256 }2708 }
22572709
2258 fn getOsoModTime(sf: StabFile, object: *const Object) u64 {2710 fn getOsoModTime(sf: StabFile, object: Object) u64 {
2259 const nlist = object.symtab.items(.nlist)[sf.comp_dir + 2];2711 const nlist = object.symtab.items(.nlist)[sf.comp_dir + 2];
2260 return nlist.n_value;2712 return nlist.n_value;
2261 }2713 }
22622714
2263 const Stab = struct {2715 const Stab = struct {
2264 is_func: bool = true,2716 is_func: bool = true,
2265 symbol: ?Symbol.Index = null,2717 index: ?Symbol.Index = null,
2718
2719 fn getSymbol(stab: Stab, object: Object) ?Symbol {
2720 const index = stab.index orelse return null;
2721 return object.symbols.items[index];
2722 }
2723
2724 pub fn format(
2725 stab: Stab,
2726 comptime unused_fmt_string: []const u8,
2727 options: std.fmt.FormatOptions,
2728 writer: anytype,
2729 ) !void {
2730 _ = stab;
2731 _ = unused_fmt_string;
2732 _ = options;
2733 _ = writer;
2734 @compileError("do not format stabs directly");
2735 }
22662736
2267 fn getSymbol(stab: Stab, macho_file: *MachO) ?*Symbol {2737 const StabFormatContext = struct { Stab, Object };
2268 return if (stab.symbol) |s| macho_file.getSymbol(s) else null;2738
2739 pub fn fmt(stab: Stab, object: Object) std.fmt.Formatter(format2) {
2740 return .{ .data = .{ stab, object } };
2741 }
2742
2743 fn format2(
2744 ctx: StabFormatContext,
2745 comptime unused_fmt_string: []const u8,
2746 options: std.fmt.FormatOptions,
2747 writer: anytype,
2748 ) !void {
2749 _ = unused_fmt_string;
2750 _ = options;
2751 const stab, const object = ctx;
2752 const sym = stab.getSymbol(object).?;
2753 if (stab.is_func) {
2754 try writer.print("func({d})", .{stab.index.?});
2755 } else if (sym.visibility == .global) {
2756 try writer.print("gsym({d})", .{stab.index.?});
2757 } else {
2758 try writer.print("stsym({d})", .{stab.index.?});
2759 }
2269 }2760 }
2270 };2761 };
2271};2762};
...@@ -2274,43 +2765,52 @@ const CompileUnit = struct {...@@ -2274,43 +2765,52 @@ const CompileUnit = struct {
2274 comp_dir: u32,2765 comp_dir: u32,
2275 tu_name: u32,2766 tu_name: u32,
22762767
2277 fn getCompDir(cu: CompileUnit, object: *const Object) [:0]const u8 {2768 fn getCompDir(cu: CompileUnit, object: Object) [:0]const u8 {
2278 return object.getString(cu.comp_dir);2769 return object.getString(cu.comp_dir);
2279 }2770 }
22802771
2281 fn getTuName(cu: CompileUnit, object: *const Object) [:0]const u8 {2772 fn getTuName(cu: CompileUnit, object: Object) [:0]const u8 {
2282 return object.getString(cu.tu_name);2773 return object.getString(cu.tu_name);
2283 }2774 }
2284};2775};
22852776
2286const InArchive = struct {2777const InArchive = struct {
2287 path: []const u8,2778 path: []const u8,
2288 offset: u64,
2289 size: u32,2779 size: u32,
2290};2780};
22912781
2782const CompactUnwindCtx = struct {
2783 rec_index: u32 = 0,
2784 rec_count: u32 = 0,
2785 reloc_index: u32 = 0,
2786 reloc_count: u32 = 0,
2787};
2788
2292const x86_64 = struct {2789const x86_64 = struct {
2293 fn parseRelocs(2790 fn parseRelocs(
2294 self: *const Object,2791 self: *Object,
2295 n_sect: u8,
2296 sect: macho.section_64,2792 sect: macho.section_64,
2297 out: *std.ArrayListUnmanaged(Relocation),2793 out: *std.ArrayListUnmanaged(Relocation),
2794 handle: File.Handle,
2298 macho_file: *MachO,2795 macho_file: *MachO,
2299 ) !void {2796 ) !void {
2300 const gpa = macho_file.base.comp.gpa;2797 const gpa = macho_file.base.comp.gpa;
23012798
2302 const handle = macho_file.getFileHandle(self.file_handle);
2303 const offset = if (self.archive) |ar| ar.offset else 0;
2304 const relocs_buffer = try gpa.alloc(u8, sect.nreloc * @sizeOf(macho.relocation_info));2799 const relocs_buffer = try gpa.alloc(u8, sect.nreloc * @sizeOf(macho.relocation_info));
2305 defer gpa.free(relocs_buffer);2800 defer gpa.free(relocs_buffer);
2306 {2801 {
2307 const amt = try handle.preadAll(relocs_buffer, sect.reloff + offset);2802 const amt = try handle.preadAll(relocs_buffer, sect.reloff + self.offset);
2308 if (amt != relocs_buffer.len) return error.InputOutput;2803 if (amt != relocs_buffer.len) return error.InputOutput;
2309 }2804 }
2310 const relocs = @as([*]align(1) const macho.relocation_info, @ptrCast(relocs_buffer.ptr))[0..sect.nreloc];2805 const relocs = @as([*]align(1) const macho.relocation_info, @ptrCast(relocs_buffer.ptr))[0..sect.nreloc];
23112806
2312 const code = try self.getSectionData(@intCast(n_sect), macho_file);2807 const sect_size = math.cast(usize, sect.size) orelse return error.Overflow;
2808 const code = try gpa.alloc(u8, sect_size);
2313 defer gpa.free(code);2809 defer gpa.free(code);
2810 {
2811 const amt = try handle.preadAll(code, sect.offset + self.offset);
2812 if (amt != code.len) return error.InputOutput;
2813 }
23142814
2315 try out.ensureTotalCapacityPrecise(gpa, relocs.len);2815 try out.ensureTotalCapacityPrecise(gpa, relocs.len);
23162816
...@@ -2332,8 +2832,9 @@ const x86_64 = struct {...@@ -2332,8 +2832,9 @@ const x86_64 = struct {
2332 .X86_64_RELOC_SIGNED_4 => 4,2832 .X86_64_RELOC_SIGNED_4 => 4,
2333 else => 0,2833 else => 0,
2334 };2834 };
2835 var is_extern = rel.r_extern == 1;
23352836
2336 const target = if (rel.r_extern == 0) blk: {2837 const target = if (!is_extern) blk: {
2337 const nsect = rel.r_symbolnum - 1;2838 const nsect = rel.r_symbolnum - 1;
2338 const taddr: i64 = if (rel.r_pcrel == 1)2839 const taddr: i64 = if (rel.r_pcrel == 1)
2339 @as(i64, @intCast(sect.addr)) + rel.r_address + addend + 42840 @as(i64, @intCast(sect.addr)) + rel.r_address + addend + 4
...@@ -2345,9 +2846,15 @@ const x86_64 = struct {...@@ -2345,9 +2846,15 @@ const x86_64 = struct {
2345 });2846 });
2346 return error.MalformedObject;2847 return error.MalformedObject;
2347 };2848 };
2348 addend = taddr - @as(i64, @intCast(macho_file.getAtom(target).?.getInputAddress(macho_file)));2849 const target_atom = self.getAtom(target).?;
2850 addend = taddr - @as(i64, @intCast(target_atom.getInputAddress(macho_file)));
2851 const isec = target_atom.getInputSection(macho_file);
2852 if (isCstringLiteral(isec) or isFixedSizeLiteral(isec) or isPtrLiteral(isec)) {
2853 is_extern = true;
2854 break :blk target_atom.getExtra(macho_file).literal_symbol_index;
2855 }
2349 break :blk target;2856 break :blk target;
2350 } else self.symbols.items[rel.r_symbolnum];2857 } else rel.r_symbolnum;
23512858
2352 const has_subtractor = if (i > 0 and2859 const has_subtractor = if (i > 0 and
2353 @as(macho.reloc_type_x86_64, @enumFromInt(relocs[i - 1].r_type)) == .X86_64_RELOC_SUBTRACTOR)2860 @as(macho.reloc_type_x86_64, @enumFromInt(relocs[i - 1].r_type)) == .X86_64_RELOC_SUBTRACTOR)
...@@ -2361,7 +2868,7 @@ const x86_64 = struct {...@@ -2361,7 +2868,7 @@ const x86_64 = struct {
2361 break :blk true;2868 break :blk true;
2362 } else false;2869 } else false;
23632870
2364 const @"type": Relocation.Type = validateRelocType(rel, rel_type) catch |err| {2871 const @"type": Relocation.Type = validateRelocType(rel, rel_type, is_extern) catch |err| {
2365 switch (err) {2872 switch (err) {
2366 error.Pcrel => try macho_file.reportParseError2(2873 error.Pcrel => try macho_file.reportParseError2(
2367 self.index,2874 self.index,
...@@ -2388,7 +2895,7 @@ const x86_64 = struct {...@@ -2388,7 +2895,7 @@ const x86_64 = struct {
2388 };2895 };
23892896
2390 out.appendAssumeCapacity(.{2897 out.appendAssumeCapacity(.{
2391 .tag = if (rel.r_extern == 1) .@"extern" else .local,2898 .tag = if (is_extern) .@"extern" else .local,
2392 .offset = @as(u32, @intCast(rel.r_address)),2899 .offset = @as(u32, @intCast(rel.r_address)),
2393 .target = target,2900 .target = target,
2394 .addend = addend,2901 .addend = addend,
...@@ -2403,7 +2910,7 @@ const x86_64 = struct {...@@ -2403,7 +2910,7 @@ const x86_64 = struct {
2403 }2910 }
2404 }2911 }
24052912
2406 fn validateRelocType(rel: macho.relocation_info, rel_type: macho.reloc_type_x86_64) !Relocation.Type {2913 fn validateRelocType(rel: macho.relocation_info, rel_type: macho.reloc_type_x86_64, is_extern: bool) !Relocation.Type {
2407 switch (rel_type) {2914 switch (rel_type) {
2408 .X86_64_RELOC_UNSIGNED => {2915 .X86_64_RELOC_UNSIGNED => {
2409 if (rel.r_pcrel == 1) return error.Pcrel;2916 if (rel.r_pcrel == 1) return error.Pcrel;
...@@ -2423,7 +2930,7 @@ const x86_64 = struct {...@@ -2423,7 +2930,7 @@ const x86_64 = struct {
2423 => {2930 => {
2424 if (rel.r_pcrel == 0) return error.NonPcrel;2931 if (rel.r_pcrel == 0) return error.NonPcrel;
2425 if (rel.r_length != 2) return error.InvalidLength;2932 if (rel.r_length != 2) return error.InvalidLength;
2426 if (rel.r_extern == 0) return error.NonExtern;2933 if (!is_extern) return error.NonExtern;
2427 return switch (rel_type) {2934 return switch (rel_type) {
2428 .X86_64_RELOC_BRANCH => .branch,2935 .X86_64_RELOC_BRANCH => .branch,
2429 .X86_64_RELOC_GOT_LOAD => .got_load,2936 .X86_64_RELOC_GOT_LOAD => .got_load,
...@@ -2454,26 +2961,29 @@ const x86_64 = struct {...@@ -2454,26 +2961,29 @@ const x86_64 = struct {
24542961
2455const aarch64 = struct {2962const aarch64 = struct {
2456 fn parseRelocs(2963 fn parseRelocs(
2457 self: *const Object,2964 self: *Object,
2458 n_sect: u8,
2459 sect: macho.section_64,2965 sect: macho.section_64,
2460 out: *std.ArrayListUnmanaged(Relocation),2966 out: *std.ArrayListUnmanaged(Relocation),
2967 handle: File.Handle,
2461 macho_file: *MachO,2968 macho_file: *MachO,
2462 ) !void {2969 ) !void {
2463 const gpa = macho_file.base.comp.gpa;2970 const gpa = macho_file.base.comp.gpa;
24642971
2465 const handle = macho_file.getFileHandle(self.file_handle);
2466 const offset = if (self.archive) |ar| ar.offset else 0;
2467 const relocs_buffer = try gpa.alloc(u8, sect.nreloc * @sizeOf(macho.relocation_info));2972 const relocs_buffer = try gpa.alloc(u8, sect.nreloc * @sizeOf(macho.relocation_info));
2468 defer gpa.free(relocs_buffer);2973 defer gpa.free(relocs_buffer);
2469 {2974 {
2470 const amt = try handle.preadAll(relocs_buffer, sect.reloff + offset);2975 const amt = try handle.preadAll(relocs_buffer, sect.reloff + self.offset);
2471 if (amt != relocs_buffer.len) return error.InputOutput;2976 if (amt != relocs_buffer.len) return error.InputOutput;
2472 }2977 }
2473 const relocs = @as([*]align(1) const macho.relocation_info, @ptrCast(relocs_buffer.ptr))[0..sect.nreloc];2978 const relocs = @as([*]align(1) const macho.relocation_info, @ptrCast(relocs_buffer.ptr))[0..sect.nreloc];
24742979
2475 const code = try self.getSectionData(@intCast(n_sect), macho_file);2980 const sect_size = math.cast(usize, sect.size) orelse return error.Overflow;
2981 const code = try gpa.alloc(u8, sect_size);
2476 defer gpa.free(code);2982 defer gpa.free(code);
2983 {
2984 const amt = try handle.preadAll(code, sect.offset + self.offset);
2985 if (amt != code.len) return error.InputOutput;
2986 }
24772987
2478 try out.ensureTotalCapacityPrecise(gpa, relocs.len);2988 try out.ensureTotalCapacityPrecise(gpa, relocs.len);
24792989
...@@ -2519,8 +3029,9 @@ const aarch64 = struct {...@@ -2519,8 +3029,9 @@ const aarch64 = struct {
2519 }3029 }
25203030
2521 const rel_type: macho.reloc_type_arm64 = @enumFromInt(rel.r_type);3031 const rel_type: macho.reloc_type_arm64 = @enumFromInt(rel.r_type);
3032 var is_extern = rel.r_extern == 1;
25223033
2523 const target = if (rel.r_extern == 0) blk: {3034 const target = if (!is_extern) blk: {
2524 const nsect = rel.r_symbolnum - 1;3035 const nsect = rel.r_symbolnum - 1;
2525 const taddr: i64 = if (rel.r_pcrel == 1)3036 const taddr: i64 = if (rel.r_pcrel == 1)
2526 @as(i64, @intCast(sect.addr)) + rel.r_address + addend3037 @as(i64, @intCast(sect.addr)) + rel.r_address + addend
...@@ -2532,9 +3043,15 @@ const aarch64 = struct {...@@ -2532,9 +3043,15 @@ const aarch64 = struct {
2532 });3043 });
2533 return error.MalformedObject;3044 return error.MalformedObject;
2534 };3045 };
2535 addend = taddr - @as(i64, @intCast(macho_file.getAtom(target).?.getInputAddress(macho_file)));3046 const target_atom = self.getAtom(target).?;
3047 addend = taddr - @as(i64, @intCast(target_atom.getInputAddress(macho_file)));
3048 const isec = target_atom.getInputSection(macho_file);
3049 if (isCstringLiteral(isec) or isFixedSizeLiteral(isec) or isPtrLiteral(isec)) {
3050 is_extern = true;
3051 break :blk target_atom.getExtra(macho_file).literal_symbol_index;
3052 }
2536 break :blk target;3053 break :blk target;
2537 } else self.symbols.items[rel.r_symbolnum];3054 } else rel.r_symbolnum;
25383055
2539 const has_subtractor = if (i > 0 and3056 const has_subtractor = if (i > 0 and
2540 @as(macho.reloc_type_arm64, @enumFromInt(relocs[i - 1].r_type)) == .ARM64_RELOC_SUBTRACTOR)3057 @as(macho.reloc_type_arm64, @enumFromInt(relocs[i - 1].r_type)) == .ARM64_RELOC_SUBTRACTOR)
...@@ -2548,7 +3065,7 @@ const aarch64 = struct {...@@ -2548,7 +3065,7 @@ const aarch64 = struct {
2548 break :blk true;3065 break :blk true;
2549 } else false;3066 } else false;
25503067
2551 const @"type": Relocation.Type = validateRelocType(rel, rel_type) catch |err| {3068 const @"type": Relocation.Type = validateRelocType(rel, rel_type, is_extern) catch |err| {
2552 switch (err) {3069 switch (err) {
2553 error.Pcrel => try macho_file.reportParseError2(3070 error.Pcrel => try macho_file.reportParseError2(
2554 self.index,3071 self.index,
...@@ -2575,7 +3092,7 @@ const aarch64 = struct {...@@ -2575,7 +3092,7 @@ const aarch64 = struct {
2575 };3092 };
25763093
2577 out.appendAssumeCapacity(.{3094 out.appendAssumeCapacity(.{
2578 .tag = if (rel.r_extern == 1) .@"extern" else .local,3095 .tag = if (is_extern) .@"extern" else .local,
2579 .offset = @as(u32, @intCast(rel.r_address)),3096 .offset = @as(u32, @intCast(rel.r_address)),
2580 .target = target,3097 .target = target,
2581 .addend = addend,3098 .addend = addend,
...@@ -2590,7 +3107,7 @@ const aarch64 = struct {...@@ -2590,7 +3107,7 @@ const aarch64 = struct {
2590 }3107 }
2591 }3108 }
25923109
2593 fn validateRelocType(rel: macho.relocation_info, rel_type: macho.reloc_type_arm64) !Relocation.Type {3110 fn validateRelocType(rel: macho.relocation_info, rel_type: macho.reloc_type_arm64, is_extern: bool) !Relocation.Type {
2594 switch (rel_type) {3111 switch (rel_type) {
2595 .ARM64_RELOC_UNSIGNED => {3112 .ARM64_RELOC_UNSIGNED => {
2596 if (rel.r_pcrel == 1) return error.Pcrel;3113 if (rel.r_pcrel == 1) return error.Pcrel;
...@@ -2611,7 +3128,7 @@ const aarch64 = struct {...@@ -2611,7 +3128,7 @@ const aarch64 = struct {
2611 => {3128 => {
2612 if (rel.r_pcrel == 0) return error.NonPcrel;3129 if (rel.r_pcrel == 0) return error.NonPcrel;
2613 if (rel.r_length != 2) return error.InvalidLength;3130 if (rel.r_length != 2) return error.InvalidLength;
2614 if (rel.r_extern == 0) return error.NonExtern;3131 if (!is_extern) return error.NonExtern;
2615 return switch (rel_type) {3132 return switch (rel_type) {
2616 .ARM64_RELOC_BRANCH26 => .branch,3133 .ARM64_RELOC_BRANCH26 => .branch,
2617 .ARM64_RELOC_PAGE21 => .page,3134 .ARM64_RELOC_PAGE21 => .page,
...@@ -2628,7 +3145,7 @@ const aarch64 = struct {...@@ -2628,7 +3145,7 @@ const aarch64 = struct {
2628 => {3145 => {
2629 if (rel.r_pcrel == 1) return error.Pcrel;3146 if (rel.r_pcrel == 1) return error.Pcrel;
2630 if (rel.r_length != 2) return error.InvalidLength;3147 if (rel.r_length != 2) return error.InvalidLength;
2631 if (rel.r_extern == 0) return error.NonExtern;3148 if (!is_extern) return error.NonExtern;
2632 return switch (rel_type) {3149 return switch (rel_type) {
2633 .ARM64_RELOC_PAGEOFF12 => .pageoff,3150 .ARM64_RELOC_PAGEOFF12 => .pageoff,
2634 .ARM64_RELOC_GOT_LOAD_PAGEOFF12 => .got_load_pageoff,3151 .ARM64_RELOC_GOT_LOAD_PAGEOFF12 => .got_load_pageoff,
src/link/MachO/Relocation.zig+23-11
...@@ -1,4 +1,4 @@...@@ -1,4 +1,4 @@
1tag: enum { @"extern", local },1tag: Tag,
2offset: u32,2offset: u32,
3target: u32,3target: u32,
4addend: i64,4addend: i64,
...@@ -10,34 +10,44 @@ meta: packed struct {...@@ -10,34 +10,44 @@ meta: packed struct {
10 symbolnum: u24,10 symbolnum: u24,
11},11},
1212
13pub fn getTargetSymbol(rel: Relocation, macho_file: *MachO) *Symbol {13pub fn getTargetSymbolRef(rel: Relocation, atom: Atom, macho_file: *MachO) MachO.Ref {
14 assert(rel.tag == .@"extern");14 assert(rel.tag == .@"extern");
15 return macho_file.getSymbol(rel.target);15 return atom.getFile(macho_file).getSymbolRef(rel.target, macho_file);
16}16}
1717
18pub fn getTargetAtom(rel: Relocation, macho_file: *MachO) *Atom {18pub fn getTargetSymbol(rel: Relocation, atom: Atom, macho_file: *MachO) *Symbol {
19 assert(rel.tag == .@"extern");
20 const ref = atom.getFile(macho_file).getSymbolRef(rel.target, macho_file);
21 return ref.getSymbol(macho_file).?;
22}
23
24pub fn getTargetAtom(rel: Relocation, atom: Atom, macho_file: *MachO) *Atom {
19 assert(rel.tag == .local);25 assert(rel.tag == .local);
20 return macho_file.getAtom(rel.target).?;26 return atom.getFile(macho_file).getAtom(rel.target).?;
21}27}
2228
23pub fn getTargetAddress(rel: Relocation, macho_file: *MachO) u64 {29pub fn getTargetAddress(rel: Relocation, atom: Atom, macho_file: *MachO) u64 {
24 return switch (rel.tag) {30 return switch (rel.tag) {
25 .local => rel.getTargetAtom(macho_file).getAddress(macho_file),31 .local => rel.getTargetAtom(atom, macho_file).getAddress(macho_file),
26 .@"extern" => rel.getTargetSymbol(macho_file).getAddress(.{}, macho_file),32 .@"extern" => rel.getTargetSymbol(atom, macho_file).getAddress(.{}, macho_file),
27 };33 };
28}34}
2935
30pub fn getGotTargetAddress(rel: Relocation, macho_file: *MachO) u64 {36pub fn getGotTargetAddress(rel: Relocation, atom: Atom, macho_file: *MachO) u64 {
31 return switch (rel.tag) {37 return switch (rel.tag) {
32 .local => 0,38 .local => 0,
33 .@"extern" => rel.getTargetSymbol(macho_file).getGotAddress(macho_file),39 .@"extern" => rel.getTargetSymbol(atom, macho_file).getGotAddress(macho_file),
34 };40 };
35}41}
3642
37pub fn getZigGotTargetAddress(rel: Relocation, macho_file: *MachO) u64 {43pub fn getZigGotTargetAddress(rel: Relocation, macho_file: *MachO) u64 {
44 const zo = macho_file.getZigObject() orelse return 0;
38 return switch (rel.tag) {45 return switch (rel.tag) {
39 .local => 0,46 .local => 0,
40 .@"extern" => rel.getTargetSymbol(macho_file).getZigGotAddress(macho_file),47 .@"extern" => {
48 const ref = zo.getSymbolRef(rel.target, macho_file);
49 return ref.getSymbol(macho_file).?.getZigGotAddress(macho_file);
50 },
41 };51 };
42}52}
4353
...@@ -155,6 +165,8 @@ pub const Type = enum {...@@ -155,6 +165,8 @@ pub const Type = enum {
155 unsigned,165 unsigned,
156};166};
157167
168const Tag = enum { local, @"extern" };
169
158const assert = std.debug.assert;170const assert = std.debug.assert;
159const macho = std.macho;171const macho = std.macho;
160const math = std.math;172const math = std.math;
src/link/MachO/Symbol.zig+52-37
...@@ -9,17 +9,16 @@ name: u32 = 0,...@@ -9,17 +9,16 @@ name: u32 = 0,
9/// File where this symbol is defined.9/// File where this symbol is defined.
10file: File.Index = 0,10file: File.Index = 0,
1111
12/// Atom containing this symbol if any.12/// Reference to Atom containing this symbol if any.
13/// Index of 0 means there is no associated atom with this symbol.
14/// Use `getAtom` to get the pointer to the atom.13/// Use `getAtom` to get the pointer to the atom.
15atom: Atom.Index = 0,14atom_ref: MachO.Ref = .{ .index = 0, .file = 0 },
1615
17/// Assigned output section index for this symbol.16/// Assigned output section index for this symbol.
18out_n_sect: u8 = 0,17out_n_sect: u8 = 0,
1918
20/// Index of the source nlist this symbol references.19/// Index of the source nlist this symbol references.
21/// Use `getNlist` to pull the nlist from the relevant file.20/// Use `getNlist` to pull the nlist from the relevant file.
22nlist_idx: Index = 0,21nlist_idx: u32 = 0,
2322
24/// Misc flags for the symbol packaged as packed struct for compression.23/// Misc flags for the symbol packaged as packed struct for compression.
25flags: Flags = .{},24flags: Flags = .{},
...@@ -55,16 +54,19 @@ pub fn weakRef(symbol: Symbol, macho_file: *MachO) bool {...@@ -55,16 +54,19 @@ pub fn weakRef(symbol: Symbol, macho_file: *MachO) bool {
55}54}
5655
57pub fn getName(symbol: Symbol, macho_file: *MachO) [:0]const u8 {56pub fn getName(symbol: Symbol, macho_file: *MachO) [:0]const u8 {
58 if (symbol.flags.global) return macho_file.strings.getAssumeExists(symbol.name);
59 return switch (symbol.getFile(macho_file).?) {57 return switch (symbol.getFile(macho_file).?) {
60 .dylib => unreachable, // There are no local symbols for dylibs
61 .zig_object => |x| x.strtab.getAssumeExists(symbol.name),58 .zig_object => |x| x.strtab.getAssumeExists(symbol.name),
62 inline else => |x| x.getString(symbol.name),59 inline else => |x| x.getString(symbol.name),
63 };60 };
64}61}
6562
66pub fn getAtom(symbol: Symbol, macho_file: *MachO) ?*Atom {63pub fn getAtom(symbol: Symbol, macho_file: *MachO) ?*Atom {
67 return macho_file.getAtom(symbol.atom);64 return symbol.atom_ref.getAtom(macho_file);
65}
66
67pub fn getOutputSectionIndex(symbol: Symbol, macho_file: *MachO) u8 {
68 if (symbol.getAtom(macho_file)) |atom| return atom.out_n_sect;
69 return symbol.out_n_sect;
68}70}
6971
70pub fn getFile(symbol: Symbol, macho_file: *MachO) ?File {72pub fn getFile(symbol: Symbol, macho_file: *MachO) ?File {
...@@ -75,8 +77,10 @@ pub fn getFile(symbol: Symbol, macho_file: *MachO) ?File {...@@ -75,8 +77,10 @@ pub fn getFile(symbol: Symbol, macho_file: *MachO) ?File {
75pub fn getNlist(symbol: Symbol, macho_file: *MachO) macho.nlist_64 {77pub fn getNlist(symbol: Symbol, macho_file: *MachO) macho.nlist_64 {
76 const file = symbol.getFile(macho_file).?;78 const file = symbol.getFile(macho_file).?;
77 return switch (file) {79 return switch (file) {
80 .dylib => unreachable,
81 .zig_object => unreachable,
78 .object => |x| x.symtab.items(.nlist)[symbol.nlist_idx],82 .object => |x| x.symtab.items(.nlist)[symbol.nlist_idx],
79 else => unreachable,83 .internal => |x| x.symtab.items[symbol.nlist_idx],
80 };84 };
81}85}
8286
...@@ -124,33 +128,35 @@ pub fn getAddress(symbol: Symbol, opts: struct {...@@ -124,33 +128,35 @@ pub fn getAddress(symbol: Symbol, opts: struct {
124128
125pub fn getGotAddress(symbol: Symbol, macho_file: *MachO) u64 {129pub fn getGotAddress(symbol: Symbol, macho_file: *MachO) u64 {
126 if (!symbol.flags.has_got) return 0;130 if (!symbol.flags.has_got) return 0;
127 const extra = symbol.getExtra(macho_file).?;131 const extra = symbol.getExtra(macho_file);
128 return macho_file.got.getAddress(extra.got, macho_file);132 return macho_file.got.getAddress(extra.got, macho_file);
129}133}
130134
131pub fn getStubsAddress(symbol: Symbol, macho_file: *MachO) u64 {135pub fn getStubsAddress(symbol: Symbol, macho_file: *MachO) u64 {
132 if (!symbol.flags.stubs) return 0;136 if (!symbol.flags.stubs) return 0;
133 const extra = symbol.getExtra(macho_file).?;137 const extra = symbol.getExtra(macho_file);
134 return macho_file.stubs.getAddress(extra.stubs, macho_file);138 return macho_file.stubs.getAddress(extra.stubs, macho_file);
135}139}
136140
137pub fn getObjcStubsAddress(symbol: Symbol, macho_file: *MachO) u64 {141pub fn getObjcStubsAddress(symbol: Symbol, macho_file: *MachO) u64 {
138 if (!symbol.flags.objc_stubs) return 0;142 if (!symbol.flags.objc_stubs) return 0;
139 const extra = symbol.getExtra(macho_file).?;143 const extra = symbol.getExtra(macho_file);
140 return macho_file.objc_stubs.getAddress(extra.objc_stubs, macho_file);144 return macho_file.objc_stubs.getAddress(extra.objc_stubs, macho_file);
141}145}
142146
143pub fn getObjcSelrefsAddress(symbol: Symbol, macho_file: *MachO) u64 {147pub fn getObjcSelrefsAddress(symbol: Symbol, macho_file: *MachO) u64 {
144 if (!symbol.flags.objc_stubs) return 0;148 if (!symbol.flags.objc_stubs) return 0;
145 const extra = symbol.getExtra(macho_file).?;149 const extra = symbol.getExtra(macho_file);
146 const atom = macho_file.getAtom(extra.objc_selrefs).?;150 const file = symbol.getFile(macho_file).?;
147 assert(atom.flags.alive);151 return switch (file) {
148 return atom.getAddress(macho_file);152 .dylib, .zig_object => unreachable,
153 inline else => |x| x.symbols.items[extra.objc_selrefs].getAddress(.{}, macho_file),
154 };
149}155}
150156
151pub fn getTlvPtrAddress(symbol: Symbol, macho_file: *MachO) u64 {157pub fn getTlvPtrAddress(symbol: Symbol, macho_file: *MachO) u64 {
152 if (!symbol.flags.tlv_ptr) return 0;158 if (!symbol.flags.tlv_ptr) return 0;
153 const extra = symbol.getExtra(macho_file).?;159 const extra = symbol.getExtra(macho_file);
154 return macho_file.tlv_ptr.getAddress(extra.tlv_ptr, macho_file);160 return macho_file.tlv_ptr.getAddress(extra.tlv_ptr, macho_file);
155}161}
156162
...@@ -162,14 +168,14 @@ const GetOrCreateZigGotEntryResult = struct {...@@ -162,14 +168,14 @@ const GetOrCreateZigGotEntryResult = struct {
162pub fn getOrCreateZigGotEntry(symbol: *Symbol, symbol_index: Index, macho_file: *MachO) !GetOrCreateZigGotEntryResult {168pub fn getOrCreateZigGotEntry(symbol: *Symbol, symbol_index: Index, macho_file: *MachO) !GetOrCreateZigGotEntryResult {
163 assert(!macho_file.base.isRelocatable());169 assert(!macho_file.base.isRelocatable());
164 assert(symbol.flags.needs_zig_got);170 assert(symbol.flags.needs_zig_got);
165 if (symbol.flags.has_zig_got) return .{ .found_existing = true, .index = symbol.getExtra(macho_file).?.zig_got };171 if (symbol.flags.has_zig_got) return .{ .found_existing = true, .index = symbol.getExtra(macho_file).zig_got };
166 const index = try macho_file.zig_got.addSymbol(symbol_index, macho_file);172 const index = try macho_file.zig_got.addSymbol(symbol_index, macho_file);
167 return .{ .found_existing = false, .index = index };173 return .{ .found_existing = false, .index = index };
168}174}
169175
170pub fn getZigGotAddress(symbol: Symbol, macho_file: *MachO) u64 {176pub fn getZigGotAddress(symbol: Symbol, macho_file: *MachO) u64 {
171 if (!symbol.flags.has_zig_got) return 0;177 if (!symbol.flags.has_zig_got) return 0;
172 const extras = symbol.getExtra(macho_file).?;178 const extras = symbol.getExtra(macho_file);
173 return macho_file.zig_got.entryAddress(extras.zig_got, macho_file);179 return macho_file.zig_got.entryAddress(extras.zig_got, macho_file);
174}180}
175181
...@@ -180,7 +186,7 @@ pub fn getOutputSymtabIndex(symbol: Symbol, macho_file: *MachO) ?u32 {...@@ -180,7 +186,7 @@ pub fn getOutputSymtabIndex(symbol: Symbol, macho_file: *MachO) ?u32 {
180 const symtab_ctx = switch (file) {186 const symtab_ctx = switch (file) {
181 inline else => |x| x.output_symtab_ctx,187 inline else => |x| x.output_symtab_ctx,
182 };188 };
183 var idx = symbol.getExtra(macho_file).?.symtab;189 var idx = symbol.getExtra(macho_file).symtab;
184 if (symbol.isLocal()) {190 if (symbol.isLocal()) {
185 idx += symtab_ctx.ilocal;191 idx += symtab_ctx.ilocal;
186 } else if (symbol.flags.@"export") {192 } else if (symbol.flags.@"export") {
...@@ -202,11 +208,8 @@ const AddExtraOpts = struct {...@@ -202,11 +208,8 @@ const AddExtraOpts = struct {
202 symtab: ?u32 = null,208 symtab: ?u32 = null,
203};209};
204210
205pub fn addExtra(symbol: *Symbol, opts: AddExtraOpts, macho_file: *MachO) !void {211pub fn addExtra(symbol: *Symbol, opts: AddExtraOpts, macho_file: *MachO) void {
206 if (symbol.getExtra(macho_file) == null) {212 var extra = symbol.getExtra(macho_file);
207 symbol.extra = try macho_file.addSymbolExtra(.{});
208 }
209 var extra = symbol.getExtra(macho_file).?;
210 inline for (@typeInfo(@TypeOf(opts)).Struct.fields) |field| {213 inline for (@typeInfo(@TypeOf(opts)).Struct.fields) |field| {
211 if (@field(opts, field.name)) |x| {214 if (@field(opts, field.name)) |x| {
212 @field(extra, field.name) = x;215 @field(extra, field.name) = x;
...@@ -215,18 +218,22 @@ pub fn addExtra(symbol: *Symbol, opts: AddExtraOpts, macho_file: *MachO) !void {...@@ -215,18 +218,22 @@ pub fn addExtra(symbol: *Symbol, opts: AddExtraOpts, macho_file: *MachO) !void {
215 symbol.setExtra(extra, macho_file);218 symbol.setExtra(extra, macho_file);
216}219}
217220
218pub inline fn getExtra(symbol: Symbol, macho_file: *MachO) ?Extra {221pub inline fn getExtra(symbol: Symbol, macho_file: *MachO) Extra {
219 return macho_file.getSymbolExtra(symbol.extra);222 return switch (symbol.getFile(macho_file).?) {
223 inline else => |x| x.getSymbolExtra(symbol.extra),
224 };
220}225}
221226
222pub inline fn setExtra(symbol: Symbol, extra: Extra, macho_file: *MachO) void {227pub inline fn setExtra(symbol: Symbol, extra: Extra, macho_file: *MachO) void {
223 macho_file.setSymbolExtra(symbol.extra, extra);228 return switch (symbol.getFile(macho_file).?) {
229 inline else => |x| x.setSymbolExtra(symbol.extra, extra),
230 };
224}231}
225232
226pub fn setOutputSym(symbol: Symbol, macho_file: *MachO, out: *macho.nlist_64) void {233pub fn setOutputSym(symbol: Symbol, macho_file: *MachO, out: *macho.nlist_64) void {
227 if (symbol.isLocal()) {234 if (symbol.isLocal()) {
228 out.n_type = if (symbol.flags.abs) macho.N_ABS else macho.N_SECT;235 out.n_type = if (symbol.flags.abs) macho.N_ABS else macho.N_SECT;
229 out.n_sect = if (symbol.flags.abs) 0 else @intCast(symbol.out_n_sect + 1);236 out.n_sect = if (symbol.flags.abs) 0 else @intCast(symbol.getOutputSectionIndex(macho_file) + 1);
230 out.n_desc = 0;237 out.n_desc = 0;
231 out.n_value = symbol.getAddress(.{ .stubs = false }, macho_file);238 out.n_value = symbol.getAddress(.{ .stubs = false }, macho_file);
232239
...@@ -238,7 +245,7 @@ pub fn setOutputSym(symbol: Symbol, macho_file: *MachO, out: *macho.nlist_64) vo...@@ -238,7 +245,7 @@ pub fn setOutputSym(symbol: Symbol, macho_file: *MachO, out: *macho.nlist_64) vo
238 assert(symbol.visibility == .global);245 assert(symbol.visibility == .global);
239 out.n_type = macho.N_EXT;246 out.n_type = macho.N_EXT;
240 out.n_type |= if (symbol.flags.abs) macho.N_ABS else macho.N_SECT;247 out.n_type |= if (symbol.flags.abs) macho.N_ABS else macho.N_SECT;
241 out.n_sect = if (symbol.flags.abs) 0 else @intCast(symbol.out_n_sect + 1);248 out.n_sect = if (symbol.flags.abs) 0 else @intCast(symbol.getOutputSectionIndex(macho_file) + 1);
242 out.n_value = symbol.getAddress(.{ .stubs = false }, macho_file);249 out.n_value = symbol.getAddress(.{ .stubs = false }, macho_file);
243 out.n_desc = 0;250 out.n_desc = 0;
244251
...@@ -318,15 +325,20 @@ fn format2(...@@ -318,15 +325,20 @@ fn format2(
318 symbol.getAddress(.{}, ctx.macho_file),325 symbol.getAddress(.{}, ctx.macho_file),
319 });326 });
320 if (symbol.getFile(ctx.macho_file)) |file| {327 if (symbol.getFile(ctx.macho_file)) |file| {
321 if (symbol.out_n_sect != 0) {328 if (symbol.getOutputSectionIndex(ctx.macho_file) != 0) {
322 try writer.print(" : sect({d})", .{symbol.out_n_sect});329 try writer.print(" : sect({d})", .{symbol.getOutputSectionIndex(ctx.macho_file)});
323 }330 }
324 if (symbol.getAtom(ctx.macho_file)) |atom| {331 if (symbol.getAtom(ctx.macho_file)) |atom| {
325 try writer.print(" : atom({d})", .{atom.atom_index});332 try writer.print(" : atom({d})", .{atom.atom_index});
326 }333 }
327 var buf: [2]u8 = .{'_'} ** 2;334 var buf: [3]u8 = .{'_'} ** 3;
328 if (symbol.flags.@"export") buf[0] = 'E';335 if (symbol.flags.@"export") buf[0] = 'E';
329 if (symbol.flags.import) buf[1] = 'I';336 if (symbol.flags.import) buf[1] = 'I';
337 switch (symbol.visibility) {
338 .local => buf[2] = 'L',
339 .hidden => buf[2] = 'H',
340 .global => buf[2] = 'G',
341 }
330 try writer.print(" : {s}", .{&buf});342 try writer.print(" : {s}", .{&buf});
331 if (symbol.flags.weak) try writer.writeAll(" : weak");343 if (symbol.flags.weak) try writer.writeAll(" : weak");
332 if (symbol.isSymbolStab(ctx.macho_file)) try writer.writeAll(" : stab");344 if (symbol.isSymbolStab(ctx.macho_file)) try writer.writeAll(" : stab");
...@@ -346,11 +358,6 @@ pub const Flags = packed struct {...@@ -346,11 +358,6 @@ pub const Flags = packed struct {
346 /// Whether the symbol is exported at runtime.358 /// Whether the symbol is exported at runtime.
347 @"export": bool = false,359 @"export": bool = false,
348360
349 /// Whether the symbol is effectively an extern and takes part in global
350 /// symbol resolution. Then, its name will be saved in global string interning
351 /// table.
352 global: bool = false,
353
354 /// Whether this symbol is weak.361 /// Whether this symbol is weak.
355 weak: bool = false,362 weak: bool = false,
356363
...@@ -400,6 +407,14 @@ pub const Visibility = enum {...@@ -400,6 +407,14 @@ pub const Visibility = enum {
400 global,407 global,
401 hidden,408 hidden,
402 local,409 local,
410
411 pub fn rank(vis: Visibility) u2 {
412 return switch (vis) {
413 .local => 2,
414 .hidden => 1,
415 .global => 0,
416 };
417 }
403};418};
404419
405pub const Extra = struct {420pub const Extra = struct {
src/link/MachO/UnwindInfo.zig+67-45
...@@ -1,10 +1,10 @@...@@ -1,10 +1,10 @@
1/// List of all unwind records gathered from all objects and sorted1/// List of all unwind records gathered from all objects and sorted
2/// by allocated relative function address within the section.2/// by allocated relative function address within the section.
3records: std.ArrayListUnmanaged(Record.Index) = .{},3records: std.ArrayListUnmanaged(Record.Ref) = .{},
44
5/// List of all personalities referenced by either unwind info entries5/// List of all personalities referenced by either unwind info entries
6/// or __eh_frame entries.6/// or __eh_frame entries.
7personalities: [max_personalities]Symbol.Index = undefined,7personalities: [max_personalities]MachO.Ref = undefined,
8personalities_count: u2 = 0,8personalities_count: u2 = 0,
99
10/// List of common encodings sorted in descending order with the most common first.10/// List of common encodings sorted in descending order with the most common first.
...@@ -25,10 +25,10 @@ pub fn deinit(info: *UnwindInfo, allocator: Allocator) void {...@@ -25,10 +25,10 @@ pub fn deinit(info: *UnwindInfo, allocator: Allocator) void {
25 info.lsdas_lookup.deinit(allocator);25 info.lsdas_lookup.deinit(allocator);
26}26}
2727
28fn canFold(macho_file: *MachO, lhs_index: Record.Index, rhs_index: Record.Index) bool {28fn canFold(macho_file: *MachO, lhs_ref: Record.Ref, rhs_ref: Record.Ref) bool {
29 const cpu_arch = macho_file.getTarget().cpu.arch;29 const cpu_arch = macho_file.getTarget().cpu.arch;
30 const lhs = macho_file.getUnwindRecord(lhs_index);30 const lhs = lhs_ref.getUnwindRecord(macho_file);
31 const rhs = macho_file.getUnwindRecord(rhs_index);31 const rhs = rhs_ref.getUnwindRecord(macho_file);
32 if (cpu_arch == .x86_64) {32 if (cpu_arch == .x86_64) {
33 if (lhs.enc.getMode() == @intFromEnum(macho.UNWIND_X86_64_MODE.STACK_IND) or33 if (lhs.enc.getMode() == @intFromEnum(macho.UNWIND_X86_64_MODE.STACK_IND) or
34 rhs.enc.getMode() == @intFromEnum(macho.UNWIND_X86_64_MODE.STACK_IND)) return false;34 rhs.enc.getMode() == @intFromEnum(macho.UNWIND_X86_64_MODE.STACK_IND)) return false;
...@@ -42,27 +42,31 @@ fn canFold(macho_file: *MachO, lhs_index: Record.Index, rhs_index: Record.Index)...@@ -42,27 +42,31 @@ fn canFold(macho_file: *MachO, lhs_index: Record.Index, rhs_index: Record.Index)
42}42}
4343
44pub fn generate(info: *UnwindInfo, macho_file: *MachO) !void {44pub fn generate(info: *UnwindInfo, macho_file: *MachO) !void {
45 const tracy = trace(@src());
46 defer tracy.end();
47
45 const gpa = macho_file.base.comp.gpa;48 const gpa = macho_file.base.comp.gpa;
4649
47 log.debug("generating unwind info", .{});50 log.debug("generating unwind info", .{});
4851
49 // Collect all unwind records52 // Collect all unwind records
50 for (macho_file.sections.items(.atoms)) |atoms| {53 for (macho_file.sections.items(.atoms)) |atoms| {
51 for (atoms.items) |atom_index| {54 for (atoms.items) |ref| {
52 const atom = macho_file.getAtom(atom_index) orelse continue;55 const atom = ref.getAtom(macho_file) orelse continue;
53 if (!atom.flags.alive) continue;56 if (!atom.flags.alive) continue;
54 const recs = atom.getUnwindRecords(macho_file);57 const recs = atom.getUnwindRecords(macho_file);
58 const file = atom.getFile(macho_file);
55 try info.records.ensureUnusedCapacity(gpa, recs.len);59 try info.records.ensureUnusedCapacity(gpa, recs.len);
56 for (recs) |rec| {60 for (recs) |rec| {
57 if (!macho_file.getUnwindRecord(rec).alive) continue;61 if (!file.object.getUnwindRecord(rec).alive) continue;
58 info.records.appendAssumeCapacity(rec);62 info.records.appendAssumeCapacity(.{ .record = rec, .file = file.getIndex() });
59 }63 }
60 }64 }
61 }65 }
6266
63 // Encode records67 // Encode records
64 for (info.records.items) |index| {68 for (info.records.items) |ref| {
65 const rec = macho_file.getUnwindRecord(index);69 const rec = ref.getUnwindRecord(macho_file);
66 if (rec.getFde(macho_file)) |fde| {70 if (rec.getFde(macho_file)) |fde| {
67 rec.enc.setDwarfSectionOffset(@intCast(fde.out_offset));71 rec.enc.setDwarfSectionOffset(@intCast(fde.out_offset));
68 if (fde.getLsdaAtom(macho_file)) |lsda| {72 if (fde.getLsdaAtom(macho_file)) |lsda| {
...@@ -72,27 +76,31 @@ pub fn generate(info: *UnwindInfo, macho_file: *MachO) !void {...@@ -72,27 +76,31 @@ pub fn generate(info: *UnwindInfo, macho_file: *MachO) !void {
72 }76 }
73 const cie = fde.getCie(macho_file);77 const cie = fde.getCie(macho_file);
74 if (cie.getPersonality(macho_file)) |_| {78 if (cie.getPersonality(macho_file)) |_| {
75 const personality_index = try info.getOrPutPersonalityFunction(cie.personality.?.index); // TODO handle error79 const object = cie.getObject(macho_file);
80 const sym_ref = object.getSymbolRef(cie.personality.?.index, macho_file);
81 const personality_index = try info.getOrPutPersonalityFunction(sym_ref); // TODO handle error
76 rec.enc.setPersonalityIndex(personality_index + 1);82 rec.enc.setPersonalityIndex(personality_index + 1);
77 }83 }
78 } else if (rec.getPersonality(macho_file)) |_| {84 } else if (rec.getPersonality(macho_file)) |_| {
79 const personality_index = try info.getOrPutPersonalityFunction(rec.personality.?); // TODO handle error85 const object = rec.getObject(macho_file);
86 const sym_ref = object.getSymbolRef(rec.personality.?, macho_file);
87 const personality_index = try info.getOrPutPersonalityFunction(sym_ref); // TODO handle error
80 rec.enc.setPersonalityIndex(personality_index + 1);88 rec.enc.setPersonalityIndex(personality_index + 1);
81 }89 }
82 }90 }
8391
84 // Sort by assigned relative address within each output section92 // Sort by assigned relative address within each output section
85 const sortFn = struct {93 const sortFn = struct {
86 fn sortFn(ctx: *MachO, lhs_index: Record.Index, rhs_index: Record.Index) bool {94 fn sortFn(ctx: *MachO, lhs_ref: Record.Ref, rhs_ref: Record.Ref) bool {
87 const lhs = ctx.getUnwindRecord(lhs_index);95 const lhs = lhs_ref.getUnwindRecord(ctx);
88 const rhs = ctx.getUnwindRecord(rhs_index);96 const rhs = rhs_ref.getUnwindRecord(ctx);
89 const lhsa = lhs.getAtom(ctx);97 const lhsa = lhs.getAtom(ctx);
90 const rhsa = rhs.getAtom(ctx);98 const rhsa = rhs.getAtom(ctx);
91 if (lhsa.out_n_sect == rhsa.out_n_sect) return lhs.getAtomAddress(ctx) < rhs.getAtomAddress(ctx);99 if (lhsa.out_n_sect == rhsa.out_n_sect) return lhs.getAtomAddress(ctx) < rhs.getAtomAddress(ctx);
92 return lhsa.out_n_sect < rhsa.out_n_sect;100 return lhsa.out_n_sect < rhsa.out_n_sect;
93 }101 }
94 }.sortFn;102 }.sortFn;
95 mem.sort(Record.Index, info.records.items, macho_file, sortFn);103 mem.sort(Record.Ref, info.records.items, macho_file, sortFn);
96104
97 // Fold the records105 // Fold the records
98 // Any adjacent two records that share encoding can be folded into one.106 // Any adjacent two records that share encoding can be folded into one.
...@@ -101,8 +109,8 @@ pub fn generate(info: *UnwindInfo, macho_file: *MachO) !void {...@@ -101,8 +109,8 @@ pub fn generate(info: *UnwindInfo, macho_file: *MachO) !void {
101 var j: usize = 1;109 var j: usize = 1;
102 while (j < info.records.items.len) : (j += 1) {110 while (j < info.records.items.len) : (j += 1) {
103 if (canFold(macho_file, info.records.items[i], info.records.items[j])) {111 if (canFold(macho_file, info.records.items[i], info.records.items[j])) {
104 const rec = macho_file.getUnwindRecord(info.records.items[i]);112 const rec = info.records.items[i].getUnwindRecord(macho_file);
105 rec.length += macho_file.getUnwindRecord(info.records.items[j]).length + 1;113 rec.length += info.records.items[j].getUnwindRecord(macho_file).length + 1;
106 } else {114 } else {
107 i += 1;115 i += 1;
108 info.records.items[i] = info.records.items[j];116 info.records.items[i] = info.records.items[j];
...@@ -111,14 +119,15 @@ pub fn generate(info: *UnwindInfo, macho_file: *MachO) !void {...@@ -111,14 +119,15 @@ pub fn generate(info: *UnwindInfo, macho_file: *MachO) !void {
111 info.records.shrinkAndFree(gpa, i + 1);119 info.records.shrinkAndFree(gpa, i + 1);
112 }120 }
113121
114 for (info.records.items) |rec_index| {122 for (info.records.items) |ref| {
115 const rec = macho_file.getUnwindRecord(rec_index);123 const rec = ref.getUnwindRecord(macho_file);
116 const atom = rec.getAtom(macho_file);124 const atom = rec.getAtom(macho_file);
117 log.debug("@{x}-{x} : {s} : rec({d}) : {}", .{125 log.debug("@{x}-{x} : {s} : rec({d}) : object({d}) : {}", .{
118 rec.getAtomAddress(macho_file),126 rec.getAtomAddress(macho_file),
119 rec.getAtomAddress(macho_file) + rec.length,127 rec.getAtomAddress(macho_file) + rec.length,
120 atom.getName(macho_file),128 atom.getName(macho_file),
121 rec_index,129 ref.record,
130 ref.file,
122 rec.enc,131 rec.enc,
123 });132 });
124 }133 }
...@@ -161,8 +170,8 @@ pub fn generate(info: *UnwindInfo, macho_file: *MachO) !void {...@@ -161,8 +170,8 @@ pub fn generate(info: *UnwindInfo, macho_file: *MachO) !void {
161 ).init(gpa);170 ).init(gpa);
162 defer common_encodings_counts.deinit();171 defer common_encodings_counts.deinit();
163172
164 for (info.records.items) |rec_index| {173 for (info.records.items) |ref| {
165 const rec = macho_file.getUnwindRecord(rec_index);174 const rec = ref.getUnwindRecord(macho_file);
166 if (rec.enc.isDwarf(macho_file)) continue;175 if (rec.enc.isDwarf(macho_file)) continue;
167 const gop = try common_encodings_counts.getOrPut(rec.enc);176 const gop = try common_encodings_counts.getOrPut(rec.enc);
168 if (!gop.found_existing) {177 if (!gop.found_existing) {
...@@ -190,7 +199,7 @@ pub fn generate(info: *UnwindInfo, macho_file: *MachO) !void {...@@ -190,7 +199,7 @@ pub fn generate(info: *UnwindInfo, macho_file: *MachO) !void {
190 {199 {
191 var i: u32 = 0;200 var i: u32 = 0;
192 while (i < info.records.items.len) {201 while (i < info.records.items.len) {
193 const rec = macho_file.getUnwindRecord(info.records.items[i]);202 const rec = info.records.items[i].getUnwindRecord(macho_file);
194 const range_start_max: u64 = rec.getAtomAddress(macho_file) + compressed_entry_func_offset_mask;203 const range_start_max: u64 = rec.getAtomAddress(macho_file) + compressed_entry_func_offset_mask;
195 var encoding_count: u9 = info.common_encodings_count;204 var encoding_count: u9 = info.common_encodings_count;
196 var space_left: u32 = second_level_page_words -205 var space_left: u32 = second_level_page_words -
...@@ -202,7 +211,7 @@ pub fn generate(info: *UnwindInfo, macho_file: *MachO) !void {...@@ -202,7 +211,7 @@ pub fn generate(info: *UnwindInfo, macho_file: *MachO) !void {
202 };211 };
203212
204 while (space_left >= 1 and i < info.records.items.len) {213 while (space_left >= 1 and i < info.records.items.len) {
205 const next = macho_file.getUnwindRecord(info.records.items[i]);214 const next = info.records.items[i].getUnwindRecord(macho_file);
206 const is_dwarf = next.enc.isDwarf(macho_file);215 const is_dwarf = next.enc.isDwarf(macho_file);
207216
208 if (next.getAtomAddress(macho_file) >= range_start_max) {217 if (next.getAtomAddress(macho_file) >= range_start_max) {
...@@ -244,8 +253,8 @@ pub fn generate(info: *UnwindInfo, macho_file: *MachO) !void {...@@ -244,8 +253,8 @@ pub fn generate(info: *UnwindInfo, macho_file: *MachO) !void {
244 // Save records having an LSDA pointer253 // Save records having an LSDA pointer
245 log.debug("LSDA pointers:", .{});254 log.debug("LSDA pointers:", .{});
246 try info.lsdas_lookup.ensureTotalCapacityPrecise(gpa, info.records.items.len);255 try info.lsdas_lookup.ensureTotalCapacityPrecise(gpa, info.records.items.len);
247 for (info.records.items, 0..) |index, i| {256 for (info.records.items, 0..) |ref, i| {
248 const rec = macho_file.getUnwindRecord(index);257 const rec = ref.getUnwindRecord(macho_file);
249 info.lsdas_lookup.appendAssumeCapacity(@intCast(info.lsdas.items.len));258 info.lsdas_lookup.appendAssumeCapacity(@intCast(info.lsdas.items.len));
250 if (rec.getLsdaAtom(macho_file)) |lsda| {259 if (rec.getLsdaAtom(macho_file)) |lsda| {
251 log.debug(" @{x} => lsda({d})", .{ rec.getAtomAddress(macho_file), lsda.atom_index });260 log.debug(" @{x} => lsda({d})", .{ rec.getAtomAddress(macho_file), lsda.atom_index });
...@@ -255,6 +264,9 @@ pub fn generate(info: *UnwindInfo, macho_file: *MachO) !void {...@@ -255,6 +264,9 @@ pub fn generate(info: *UnwindInfo, macho_file: *MachO) !void {
255}264}
256265
257pub fn calcSize(info: UnwindInfo) usize {266pub fn calcSize(info: UnwindInfo) usize {
267 const tracy = trace(@src());
268 defer tracy.end();
269
258 var total_size: usize = 0;270 var total_size: usize = 0;
259 total_size += @sizeOf(macho.unwind_info_section_header);271 total_size += @sizeOf(macho.unwind_info_section_header);
260 total_size +=272 total_size +=
...@@ -291,8 +303,8 @@ pub fn write(info: UnwindInfo, macho_file: *MachO, buffer: []u8) !void {...@@ -291,8 +303,8 @@ pub fn write(info: UnwindInfo, macho_file: *MachO, buffer: []u8) !void {
291303
292 try writer.writeAll(mem.sliceAsBytes(info.common_encodings[0..info.common_encodings_count]));304 try writer.writeAll(mem.sliceAsBytes(info.common_encodings[0..info.common_encodings_count]));
293305
294 for (info.personalities[0..info.personalities_count]) |sym_index| {306 for (info.personalities[0..info.personalities_count]) |ref| {
295 const sym = macho_file.getSymbol(sym_index);307 const sym = ref.getSymbol(macho_file).?;
296 try writer.writeInt(u32, @intCast(sym.getGotAddress(macho_file) - seg.vmaddr), .little);308 try writer.writeInt(u32, @intCast(sym.getGotAddress(macho_file) - seg.vmaddr), .little);
297 }309 }
298310
...@@ -301,7 +313,7 @@ pub fn write(info: UnwindInfo, macho_file: *MachO, buffer: []u8) !void {...@@ -301,7 +313,7 @@ pub fn write(info: UnwindInfo, macho_file: *MachO, buffer: []u8) !void {
301 (info.lsdas.items.len * @sizeOf(macho.unwind_info_section_header_lsda_index_entry))));313 (info.lsdas.items.len * @sizeOf(macho.unwind_info_section_header_lsda_index_entry))));
302 for (info.pages.items, 0..) |page, i| {314 for (info.pages.items, 0..) |page, i| {
303 assert(page.count > 0);315 assert(page.count > 0);
304 const rec = macho_file.getUnwindRecord(info.records.items[page.start]);316 const rec = info.records.items[page.start].getUnwindRecord(macho_file);
305 try writer.writeStruct(macho.unwind_info_section_header_index_entry{317 try writer.writeStruct(macho.unwind_info_section_header_index_entry{
306 .functionOffset = @as(u32, @intCast(rec.getAtomAddress(macho_file) - seg.vmaddr)),318 .functionOffset = @as(u32, @intCast(rec.getAtomAddress(macho_file) - seg.vmaddr)),
307 .secondLevelPagesSectionOffset = @as(u32, @intCast(pages_base_offset + i * second_level_page_bytes)),319 .secondLevelPagesSectionOffset = @as(u32, @intCast(pages_base_offset + i * second_level_page_bytes)),
...@@ -310,7 +322,7 @@ pub fn write(info: UnwindInfo, macho_file: *MachO, buffer: []u8) !void {...@@ -310,7 +322,7 @@ pub fn write(info: UnwindInfo, macho_file: *MachO, buffer: []u8) !void {
310 });322 });
311 }323 }
312324
313 const last_rec = macho_file.getUnwindRecord(info.records.items[info.records.items.len - 1]);325 const last_rec = info.records.items[info.records.items.len - 1].getUnwindRecord(macho_file);
314 const sentinel_address = @as(u32, @intCast(last_rec.getAtomAddress(macho_file) + last_rec.length - seg.vmaddr));326 const sentinel_address = @as(u32, @intCast(last_rec.getAtomAddress(macho_file) + last_rec.length - seg.vmaddr));
315 try writer.writeStruct(macho.unwind_info_section_header_index_entry{327 try writer.writeStruct(macho.unwind_info_section_header_index_entry{
316 .functionOffset = sentinel_address,328 .functionOffset = sentinel_address,
...@@ -320,7 +332,7 @@ pub fn write(info: UnwindInfo, macho_file: *MachO, buffer: []u8) !void {...@@ -320,7 +332,7 @@ pub fn write(info: UnwindInfo, macho_file: *MachO, buffer: []u8) !void {
320 });332 });
321333
322 for (info.lsdas.items) |index| {334 for (info.lsdas.items) |index| {
323 const rec = macho_file.getUnwindRecord(info.records.items[index]);335 const rec = info.records.items[index].getUnwindRecord(macho_file);
324 try writer.writeStruct(macho.unwind_info_section_header_lsda_index_entry{336 try writer.writeStruct(macho.unwind_info_section_header_lsda_index_entry{
325 .functionOffset = @as(u32, @intCast(rec.getAtomAddress(macho_file) - seg.vmaddr)),337 .functionOffset = @as(u32, @intCast(rec.getAtomAddress(macho_file) - seg.vmaddr)),
326 .lsdaOffset = @as(u32, @intCast(rec.getLsdaAddress(macho_file) - seg.vmaddr)),338 .lsdaOffset = @as(u32, @intCast(rec.getLsdaAddress(macho_file) - seg.vmaddr)),
...@@ -340,13 +352,13 @@ pub fn write(info: UnwindInfo, macho_file: *MachO, buffer: []u8) !void {...@@ -340,13 +352,13 @@ pub fn write(info: UnwindInfo, macho_file: *MachO, buffer: []u8) !void {
340 @memset(buffer[stream.pos..], 0);352 @memset(buffer[stream.pos..], 0);
341}353}
342354
343fn getOrPutPersonalityFunction(info: *UnwindInfo, sym_index: Symbol.Index) error{TooManyPersonalities}!u2 {355fn getOrPutPersonalityFunction(info: *UnwindInfo, ref: MachO.Ref) error{TooManyPersonalities}!u2 {
344 comptime var index: u2 = 0;356 comptime var index: u2 = 0;
345 inline while (index < max_personalities) : (index += 1) {357 inline while (index < max_personalities) : (index += 1) {
346 if (info.personalities[index] == sym_index) {358 if (info.personalities[index].eql(ref)) {
347 return index;359 return index;
348 } else if (index == info.personalities_count) {360 } else if (index == info.personalities_count) {
349 info.personalities[index] = sym_index;361 info.personalities[index] = ref;
350 info.personalities_count += 1;362 info.personalities_count += 1;
351 return index;363 return index;
352 }364 }
...@@ -461,16 +473,17 @@ pub const Record = struct {...@@ -461,16 +473,17 @@ pub const Record = struct {
461 }473 }
462474
463 pub fn getAtom(rec: Record, macho_file: *MachO) *Atom {475 pub fn getAtom(rec: Record, macho_file: *MachO) *Atom {
464 return macho_file.getAtom(rec.atom).?;476 return rec.getObject(macho_file).getAtom(rec.atom).?;
465 }477 }
466478
467 pub fn getLsdaAtom(rec: Record, macho_file: *MachO) ?*Atom {479 pub fn getLsdaAtom(rec: Record, macho_file: *MachO) ?*Atom {
468 return macho_file.getAtom(rec.lsda);480 return rec.getObject(macho_file).getAtom(rec.lsda);
469 }481 }
470482
471 pub fn getPersonality(rec: Record, macho_file: *MachO) ?*Symbol {483 pub fn getPersonality(rec: Record, macho_file: *MachO) ?*Symbol {
472 const personality = rec.personality orelse return null;484 const personality = rec.personality orelse return null;
473 return macho_file.getSymbol(personality);485 const object = rec.getObject(macho_file);
486 return object.getSymbolRef(personality, macho_file).getSymbol(macho_file);
474 }487 }
475488
476 pub fn getFde(rec: Record, macho_file: *MachO) ?Fde {489 pub fn getFde(rec: Record, macho_file: *MachO) ?Fde {
...@@ -537,6 +550,15 @@ pub const Record = struct {...@@ -537,6 +550,15 @@ pub const Record = struct {
537 }550 }
538551
539 pub const Index = u32;552 pub const Index = u32;
553
554 const Ref = struct {
555 record: Index,
556 file: File.Index,
557
558 pub fn getUnwindRecord(ref: Ref, macho_file: *MachO) *Record {
559 return macho_file.getFile(ref.file).?.object.getUnwindRecord(ref.record);
560 }
561 };
540};562};
541563
542const max_personalities = 3;564const max_personalities = 3;
...@@ -635,8 +657,8 @@ const Page = struct {...@@ -635,8 +657,8 @@ const Page = struct {
635 .entryCount = page.count,657 .entryCount = page.count,
636 });658 });
637659
638 for (info.records.items[page.start..][0..page.count]) |index| {660 for (info.records.items[page.start..][0..page.count]) |ref| {
639 const rec = macho_file.getUnwindRecord(index);661 const rec = ref.getUnwindRecord(macho_file);
640 try writer.writeStruct(macho.unwind_info_regular_second_level_entry{662 try writer.writeStruct(macho.unwind_info_regular_second_level_entry{
641 .functionOffset = @as(u32, @intCast(rec.getAtomAddress(macho_file) - seg.vmaddr)),663 .functionOffset = @as(u32, @intCast(rec.getAtomAddress(macho_file) - seg.vmaddr)),
642 .encoding = rec.enc.enc,664 .encoding = rec.enc.enc,
...@@ -658,9 +680,9 @@ const Page = struct {...@@ -658,9 +680,9 @@ const Page = struct {
658 }680 }
659681
660 assert(page.count > 0);682 assert(page.count > 0);
661 const first_rec = macho_file.getUnwindRecord(info.records.items[page.start]);683 const first_rec = info.records.items[page.start].getUnwindRecord(macho_file);
662 for (info.records.items[page.start..][0..page.count]) |index| {684 for (info.records.items[page.start..][0..page.count]) |ref| {
663 const rec = macho_file.getUnwindRecord(index);685 const rec = ref.getUnwindRecord(macho_file);
664 const enc_index = blk: {686 const enc_index = blk: {
665 if (info.getCommonEncoding(rec.enc)) |id| break :blk id;687 if (info.getCommonEncoding(rec.enc)) |id| break :blk id;
666 const ncommon = info.common_encodings_count;688 const ncommon = info.common_encodings_count;
src/link/MachO/ZigObject.zig+522-230
...@@ -6,9 +6,15 @@ index: File.Index,...@@ -6,9 +6,15 @@ index: File.Index,
6symtab: std.MultiArrayList(Nlist) = .{},6symtab: std.MultiArrayList(Nlist) = .{},
7strtab: StringTable = .{},7strtab: StringTable = .{},
88
9symbols: std.ArrayListUnmanaged(Symbol.Index) = .{},9symbols: std.ArrayListUnmanaged(Symbol) = .{},
10atoms: std.ArrayListUnmanaged(Atom.Index) = .{},10symbols_extra: std.ArrayListUnmanaged(u32) = .{},
11globals_lookup: std.AutoHashMapUnmanaged(u32, Symbol.Index) = .{},11globals: std.ArrayListUnmanaged(MachO.SymbolResolver.Index) = .{},
12/// Maps string index (so name) into nlist index for the global symbol defined within this
13/// module.
14globals_lookup: std.AutoHashMapUnmanaged(u32, u32) = .{},
15atoms: std.ArrayListUnmanaged(Atom) = .{},
16atoms_indexes: std.ArrayListUnmanaged(Atom.Index) = .{},
17atoms_extra: std.ArrayListUnmanaged(u32) = .{},
1218
13/// Table of tracked LazySymbols.19/// Table of tracked LazySymbols.
14lazy_syms: LazySymbolTable = .{},20lazy_syms: LazySymbolTable = .{},
...@@ -48,7 +54,6 @@ relocs: RelocationTable = .{},...@@ -48,7 +54,6 @@ relocs: RelocationTable = .{},
4854
49dwarf: ?Dwarf = null,55dwarf: ?Dwarf = null,
5056
51dynamic_relocs: MachO.DynamicRelocs = .{},
52output_symtab_ctx: MachO.SymtabCtx = .{},57output_symtab_ctx: MachO.SymtabCtx = .{},
53output_ar_state: Archive.ArState = .{},58output_ar_state: Archive.ArState = .{},
5459
...@@ -59,10 +64,13 @@ debug_info_header_dirty: bool = false,...@@ -59,10 +64,13 @@ debug_info_header_dirty: bool = false,
59debug_line_header_dirty: bool = false,64debug_line_header_dirty: bool = false,
6065
61pub fn init(self: *ZigObject, macho_file: *MachO) !void {66pub fn init(self: *ZigObject, macho_file: *MachO) !void {
67 const tracy = trace(@src());
68 defer tracy.end();
69
62 const comp = macho_file.base.comp;70 const comp = macho_file.base.comp;
63 const gpa = comp.gpa;71 const gpa = comp.gpa;
6472
65 try self.atoms.append(gpa, 0); // null input section73 try self.atoms.append(gpa, .{ .extra = try self.addAtomExtra(gpa, .{}) }); // null input section
66 try self.strtab.buffer.append(gpa, 0);74 try self.strtab.buffer.append(gpa, 0);
6775
68 switch (comp.config.debug_format) {76 switch (comp.config.debug_format) {
...@@ -85,8 +93,12 @@ pub fn deinit(self: *ZigObject, allocator: Allocator) void {...@@ -85,8 +93,12 @@ pub fn deinit(self: *ZigObject, allocator: Allocator) void {
85 self.symtab.deinit(allocator);93 self.symtab.deinit(allocator);
86 self.strtab.deinit(allocator);94 self.strtab.deinit(allocator);
87 self.symbols.deinit(allocator);95 self.symbols.deinit(allocator);
88 self.atoms.deinit(allocator);96 self.symbols_extra.deinit(allocator);
97 self.globals.deinit(allocator);
89 self.globals_lookup.deinit(allocator);98 self.globals_lookup.deinit(allocator);
99 self.atoms.deinit(allocator);
100 self.atoms_indexes.deinit(allocator);
101 self.atoms_extra.deinit(allocator);
90102
91 {103 {
92 var it = self.decls.iterator();104 var it = self.decls.iterator();
...@@ -129,53 +141,73 @@ pub fn deinit(self: *ZigObject, allocator: Allocator) void {...@@ -129,53 +141,73 @@ pub fn deinit(self: *ZigObject, allocator: Allocator) void {
129 }141 }
130}142}
131143
132fn addNlist(self: *ZigObject, allocator: Allocator) !Symbol.Index {144fn newSymbol(self: *ZigObject, allocator: Allocator, name: u32, args: struct {
145 type: u8 = macho.N_UNDF | macho.N_EXT,
146 desc: u16 = 0,
147}) !Symbol.Index {
133 try self.symtab.ensureUnusedCapacity(allocator, 1);148 try self.symtab.ensureUnusedCapacity(allocator, 1);
134 const index = @as(Symbol.Index, @intCast(self.symtab.addOneAssumeCapacity()));149 try self.symbols.ensureUnusedCapacity(allocator, 1);
135 self.symtab.set(index, .{150 try self.symbols_extra.ensureUnusedCapacity(allocator, @sizeOf(Symbol.Extra));
136 .nlist = MachO.null_sym,151 try self.globals.ensureUnusedCapacity(allocator, 1);
152
153 const index = self.addSymbolAssumeCapacity();
154 const symbol = &self.symbols.items[index];
155 symbol.name = name;
156 symbol.extra = self.addSymbolExtraAssumeCapacity(.{});
157
158 const nlist_idx: u32 = @intCast(self.symtab.addOneAssumeCapacity());
159 self.symtab.set(nlist_idx, .{
160 .nlist = .{
161 .n_strx = name,
162 .n_type = args.type,
163 .n_sect = 0,
164 .n_desc = args.desc,
165 .n_value = 0,
166 },
137 .size = 0,167 .size = 0,
138 .atom = 0,168 .atom = 0,
139 });169 });
140 return index;170 symbol.nlist_idx = nlist_idx;
141}
142
143pub fn addAtom(self: *ZigObject, macho_file: *MachO) !Symbol.Index {
144 const gpa = macho_file.base.comp.gpa;
145 const atom_index = try macho_file.addAtom();
146 const symbol_index = try macho_file.addSymbol();
147 const nlist_index = try self.addNlist(gpa);
148171
149 try self.atoms.append(gpa, atom_index);172 self.globals.appendAssumeCapacity(0);
150 try self.symbols.append(gpa, symbol_index);
151173
152 const atom = macho_file.getAtom(atom_index).?;174 return index;
153 atom.file = self.index;175}
154 atom.atom_index = atom_index;
155176
156 const symbol = macho_file.getSymbol(symbol_index);177fn newAtom(self: *ZigObject, allocator: Allocator, name: u32, macho_file: *MachO) !Atom.Index {
157 symbol.file = self.index;178 try self.atoms.ensureUnusedCapacity(allocator, 1);
158 symbol.atom = atom_index;179 try self.atoms_extra.ensureUnusedCapacity(allocator, @sizeOf(Atom.Extra));
180 try self.atoms_indexes.ensureUnusedCapacity(allocator, 1);
181 try self.relocs.ensureUnusedCapacity(allocator, 1);
159182
160 self.symtab.items(.atom)[nlist_index] = atom_index;183 const index = self.addAtomAssumeCapacity();
161 symbol.nlist_idx = nlist_index;184 self.atoms_indexes.appendAssumeCapacity(index);
185 const atom = self.getAtom(index).?;
186 atom.name = name;
162187
163 const relocs_index = @as(u32, @intCast(self.relocs.items.len));188 const relocs_index = @as(u32, @intCast(self.relocs.items.len));
164 const relocs = try self.relocs.addOne(gpa);189 self.relocs.addOneAssumeCapacity().* = .{};
165 relocs.* = .{};190 atom.addExtra(.{ .rel_index = relocs_index, .rel_count = 0 }, macho_file);
166 try atom.addExtra(.{ .rel_index = relocs_index, .rel_count = 0 }, macho_file);
167 atom.flags.relocs = true;
168191
169 return symbol_index;192 return index;
193}
194
195fn newSymbolWithAtom(self: *ZigObject, allocator: Allocator, name: u32, macho_file: *MachO) !Symbol.Index {
196 const atom_index = try self.newAtom(allocator, name, macho_file);
197 const sym_index = try self.newSymbol(allocator, name, .{ .type = macho.N_SECT });
198 const sym = &self.symbols.items[sym_index];
199 sym.atom_ref = .{ .index = atom_index, .file = self.index };
200 self.symtab.items(.atom)[sym.nlist_idx] = atom_index;
201 return sym_index;
170}202}
171203
172pub fn getAtomData(self: ZigObject, macho_file: *MachO, atom: Atom, buffer: []u8) !void {204pub fn getAtomData(self: ZigObject, macho_file: *MachO, atom: Atom, buffer: []u8) !void {
173 assert(atom.file == self.index);205 assert(atom.file == self.index);
174 assert(atom.size == buffer.len);206 assert(atom.size == buffer.len);
175 const sect = macho_file.sections.items(.header)[atom.out_n_sect];207 const isec = atom.getInputSection(macho_file);
176 assert(!sect.isZerofill());208 assert(!isec.isZerofill());
177209
178 switch (sect.type()) {210 switch (isec.type()) {
179 macho.S_THREAD_LOCAL_REGULAR => {211 macho.S_THREAD_LOCAL_REGULAR => {
180 const tlv = self.tlv_initializers.get(atom.atom_index).?;212 const tlv = self.tlv_initializers.get(atom.atom_index).?;
181 @memcpy(buffer, tlv.data);213 @memcpy(buffer, tlv.data);
...@@ -184,6 +216,7 @@ pub fn getAtomData(self: ZigObject, macho_file: *MachO, atom: Atom, buffer: []u8...@@ -184,6 +216,7 @@ pub fn getAtomData(self: ZigObject, macho_file: *MachO, atom: Atom, buffer: []u8
184 @memset(buffer, 0);216 @memset(buffer, 0);
185 },217 },
186 else => {218 else => {
219 const sect = macho_file.sections.items(.header)[atom.out_n_sect];
187 const file_offset = sect.offset + atom.value;220 const file_offset = sect.offset + atom.value;
188 const amt = try macho_file.base.file.?.preadAll(buffer, file_offset);221 const amt = try macho_file.base.file.?.preadAll(buffer, file_offset);
189 if (amt != buffer.len) return error.InputOutput;222 if (amt != buffer.len) return error.InputOutput;
...@@ -192,102 +225,65 @@ pub fn getAtomData(self: ZigObject, macho_file: *MachO, atom: Atom, buffer: []u8...@@ -192,102 +225,65 @@ pub fn getAtomData(self: ZigObject, macho_file: *MachO, atom: Atom, buffer: []u8
192}225}
193226
194pub fn getAtomRelocs(self: *ZigObject, atom: Atom, macho_file: *MachO) []const Relocation {227pub fn getAtomRelocs(self: *ZigObject, atom: Atom, macho_file: *MachO) []const Relocation {
195 if (!atom.flags.relocs) return &[0]Relocation{};228 const extra = atom.getExtra(macho_file);
196 const extra = atom.getExtra(macho_file).?;
197 const relocs = self.relocs.items[extra.rel_index];229 const relocs = self.relocs.items[extra.rel_index];
198 return relocs.items[0..extra.rel_count];230 return relocs.items[0..extra.rel_count];
199}231}
200232
201pub fn freeAtomRelocs(self: *ZigObject, atom: Atom, macho_file: *MachO) void {233pub fn freeAtomRelocs(self: *ZigObject, atom: Atom, macho_file: *MachO) void {
202 if (atom.flags.relocs) {234 const extra = atom.getExtra(macho_file);
203 const extra = atom.getExtra(macho_file).?;235 self.relocs.items[extra.rel_index].clearRetainingCapacity();
204 self.relocs.items[extra.rel_index].clearRetainingCapacity();
205 }
206}236}
207237
208pub fn resolveSymbols(self: *ZigObject, macho_file: *MachO) void {238pub fn resolveSymbols(self: *ZigObject, macho_file: *MachO) !void {
209 const tracy = trace(@src());239 const tracy = trace(@src());
210 defer tracy.end();240 defer tracy.end();
211241
212 for (self.symbols.items, 0..) |index, i| {242 const gpa = macho_file.base.comp.gpa;
213 const nlist_idx = @as(Symbol.Index, @intCast(i));
214 const nlist = self.symtab.items(.nlist)[nlist_idx];
215 const atom_index = self.symtab.items(.atom)[nlist_idx];
216243
244 for (self.symtab.items(.nlist), self.symtab.items(.atom), self.globals.items, 0..) |nlist, atom_index, *global, i| {
217 if (!nlist.ext()) continue;245 if (!nlist.ext()) continue;
218 if (nlist.undf() and !nlist.tentative()) continue;
219 if (nlist.sect()) {246 if (nlist.sect()) {
220 const atom = macho_file.getAtom(atom_index).?;247 const atom = self.getAtom(atom_index).?;
221 if (!atom.flags.alive) continue;248 if (!atom.flags.alive) continue;
222 }249 }
223250
224 const symbol = macho_file.getSymbol(index);251 const gop = try macho_file.resolver.getOrPut(gpa, .{
252 .index = @intCast(i),
253 .file = self.index,
254 }, macho_file);
255 if (!gop.found_existing) {
256 gop.ref.* = .{ .index = 0, .file = 0 };
257 }
258 global.* = gop.index;
259
260 if (nlist.undf() and !nlist.tentative()) continue;
261 if (gop.ref.getFile(macho_file) == null) {
262 gop.ref.* = .{ .index = @intCast(i), .file = self.index };
263 continue;
264 }
265
225 if (self.asFile().getSymbolRank(.{266 if (self.asFile().getSymbolRank(.{
226 .archive = false,267 .archive = false,
227 .weak = nlist.weakDef(),268 .weak = nlist.weakDef(),
228 .tentative = nlist.tentative(),269 .tentative = nlist.tentative(),
229 }) < symbol.getSymbolRank(macho_file)) {270 }) < gop.ref.getSymbol(macho_file).?.getSymbolRank(macho_file)) {
230 const value = if (nlist.sect()) blk: {271 gop.ref.* = .{ .index = @intCast(i), .file = self.index };
231 const atom = macho_file.getAtom(atom_index).?;
232 break :blk nlist.n_value - atom.getInputAddress(macho_file);
233 } else nlist.n_value;
234 const out_n_sect = if (nlist.sect()) macho_file.getAtom(atom_index).?.out_n_sect else 0;
235 symbol.value = value;
236 symbol.atom = atom_index;
237 symbol.out_n_sect = out_n_sect;
238 symbol.nlist_idx = nlist_idx;
239 symbol.file = self.index;
240 symbol.flags.weak = nlist.weakDef();
241 symbol.flags.abs = nlist.abs();
242 symbol.flags.tentative = nlist.tentative();
243 symbol.flags.weak_ref = false;
244 symbol.flags.dyn_ref = nlist.n_desc & macho.REFERENCED_DYNAMICALLY != 0;
245 symbol.flags.no_dead_strip = symbol.flags.no_dead_strip or nlist.noDeadStrip();
246 // TODO: symbol.flags.interposable = macho_file.base.isDynLib() and macho_file.options.namespace == .flat and !nlist.pext();
247 symbol.flags.interposable = false;
248
249 if (nlist.sect() and
250 macho_file.sections.items(.header)[nlist.n_sect - 1].type() == macho.S_THREAD_LOCAL_VARIABLES)
251 {
252 symbol.flags.tlv = true;
253 }
254 }
255
256 // Regardless of who the winner is, we still merge symbol visibility here.
257 if (nlist.pext() or (nlist.weakDef() and nlist.weakRef())) {
258 if (symbol.visibility != .global) {
259 symbol.visibility = .hidden;
260 }
261 } else {
262 symbol.visibility = .global;
263 }272 }
264 }273 }
265}274}
266275
267pub fn resetGlobals(self: *ZigObject, macho_file: *MachO) void {
268 for (self.symbols.items, 0..) |sym_index, nlist_idx| {
269 if (!self.symtab.items(.nlist)[nlist_idx].ext()) continue;
270 const sym = macho_file.getSymbol(sym_index);
271 const name = sym.name;
272 const global = sym.flags.global;
273 const weak_ref = sym.flags.weak_ref;
274 sym.* = .{};
275 sym.name = name;
276 sym.flags.global = global;
277 sym.flags.weak_ref = weak_ref;
278 }
279}
280
281pub fn markLive(self: *ZigObject, macho_file: *MachO) void {276pub fn markLive(self: *ZigObject, macho_file: *MachO) void {
282 const tracy = trace(@src());277 const tracy = trace(@src());
283 defer tracy.end();278 defer tracy.end();
284279
285 for (self.symbols.items, 0..) |index, nlist_idx| {280 for (0..self.symbols.items.len) |i| {
286 const nlist = self.symtab.items(.nlist)[nlist_idx];281 const nlist = self.symtab.items(.nlist)[i];
287 if (!nlist.ext()) continue;282 if (!nlist.ext()) continue;
288283
289 const sym = macho_file.getSymbol(index);284 const ref = self.getSymbolRef(@intCast(i), macho_file);
290 const file = sym.getFile(macho_file) orelse continue;285 const file = ref.getFile(macho_file) orelse continue;
286 const sym = ref.getSymbol(macho_file).?;
291 const should_keep = nlist.undf() or (nlist.tentative() and !sym.flags.tentative);287 const should_keep = nlist.undf() or (nlist.tentative() and !sym.flags.tentative);
292 if (should_keep and file == .object and !file.object.alive) {288 if (should_keep and file == .object and !file.object.alive) {
293 file.object.alive = true;289 file.object.alive = true;
...@@ -296,20 +292,18 @@ pub fn markLive(self: *ZigObject, macho_file: *MachO) void {...@@ -296,20 +292,18 @@ pub fn markLive(self: *ZigObject, macho_file: *MachO) void {
296 }292 }
297}293}
298294
299pub fn checkDuplicates(self: *ZigObject, dupes: anytype, macho_file: *MachO) !void {295pub fn mergeSymbolVisibility(self: *ZigObject, macho_file: *MachO) void {
300 for (self.symbols.items, 0..) |index, nlist_idx| {296 const tracy = trace(@src());
301 const sym = macho_file.getSymbol(index);297 defer tracy.end();
302 if (sym.visibility != .global) continue;
303 const file = sym.getFile(macho_file) orelse continue;
304 if (file.getIndex() == self.index) continue;
305298
306 const nlist = self.symtab.items(.nlist)[nlist_idx];299 for (self.symbols.items, 0..) |sym, i| {
307 if (!nlist.undf() and !nlist.tentative() and !(nlist.weakDef() or nlist.pext())) {300 const ref = self.getSymbolRef(@intCast(i), macho_file);
308 const gop = try dupes.getOrPut(index);301 const global = ref.getSymbol(macho_file) orelse continue;
309 if (!gop.found_existing) {302 if (sym.visibility.rank() < global.visibility.rank()) {
310 gop.value_ptr.* = .{};303 global.visibility = sym.visibility;
311 }304 }
312 try gop.value_ptr.append(macho_file.base.comp.gpa, self.index);305 if (sym.flags.weak_ref) {
306 global.flags.weak_ref = true;
313 }307 }
314 }308 }
315}309}
...@@ -331,7 +325,9 @@ pub fn dedupLiterals(self: *ZigObject, lp: MachO.LiteralPool, macho_file: *MachO...@@ -331,7 +325,9 @@ pub fn dedupLiterals(self: *ZigObject, lp: MachO.LiteralPool, macho_file: *MachO
331/// This is just a temporary helper function that allows us to re-read what we wrote to file into a buffer.325/// This is just a temporary helper function that allows us to re-read what we wrote to file into a buffer.
332/// We need this so that we can write to an archive.326/// We need this so that we can write to an archive.
333/// TODO implement writing ZigObject data directly to a buffer instead.327/// TODO implement writing ZigObject data directly to a buffer instead.
334pub fn readFileContents(self: *ZigObject, size: usize, macho_file: *MachO) !void {328pub fn readFileContents(self: *ZigObject, macho_file: *MachO) !void {
329 // Size of the output object file is always the offset + size of the strtab
330 const size = macho_file.symtab_cmd.stroff + macho_file.symtab_cmd.strsize;
335 const gpa = macho_file.base.comp.gpa;331 const gpa = macho_file.base.comp.gpa;
336 try self.data.resize(gpa, size);332 try self.data.resize(gpa, size);
337 const amt = try macho_file.base.file.?.preadAll(self.data.items, 0);333 const amt = try macho_file.base.file.?.preadAll(self.data.items, 0);
...@@ -340,9 +336,9 @@ pub fn readFileContents(self: *ZigObject, size: usize, macho_file: *MachO) !void...@@ -340,9 +336,9 @@ pub fn readFileContents(self: *ZigObject, size: usize, macho_file: *MachO) !void
340336
341pub fn updateArSymtab(self: ZigObject, ar_symtab: *Archive.ArSymtab, macho_file: *MachO) error{OutOfMemory}!void {337pub fn updateArSymtab(self: ZigObject, ar_symtab: *Archive.ArSymtab, macho_file: *MachO) error{OutOfMemory}!void {
342 const gpa = macho_file.base.comp.gpa;338 const gpa = macho_file.base.comp.gpa;
343 for (self.symbols.items) |sym_index| {339 for (self.symbols.items, 0..) |sym, i| {
344 const sym = macho_file.getSymbol(sym_index);340 const ref = self.getSymbolRef(@intCast(i), macho_file);
345 const file = sym.getFile(macho_file).?;341 const file = ref.getFile(macho_file).?;
346 assert(file.getIndex() == self.index);342 assert(file.getIndex() == self.index);
347 if (!sym.flags.@"export") continue;343 if (!sym.flags.@"export") continue;
348 const off = try ar_symtab.strtab.insert(gpa, sym.getName(macho_file));344 const off = try ar_symtab.strtab.insert(gpa, sym.getName(macho_file));
...@@ -362,9 +358,39 @@ pub fn writeAr(self: ZigObject, ar_format: Archive.Format, writer: anytype) !voi...@@ -362,9 +358,39 @@ pub fn writeAr(self: ZigObject, ar_format: Archive.Format, writer: anytype) !voi
362 try writer.writeAll(self.data.items);358 try writer.writeAll(self.data.items);
363}359}
364360
361pub fn claimUnresolved(self: *ZigObject, macho_file: *MachO) void {
362 const tracy = trace(@src());
363 defer tracy.end();
364
365 for (self.symbols.items, 0..) |*sym, i| {
366 const nlist = self.symtab.items(.nlist)[i];
367 if (!nlist.ext()) continue;
368 if (!nlist.undf()) continue;
369
370 if (self.getSymbolRef(@intCast(i), macho_file).getFile(macho_file) != null) continue;
371
372 const is_import = switch (macho_file.undefined_treatment) {
373 .@"error" => false,
374 .warn, .suppress => nlist.weakRef(),
375 .dynamic_lookup => true,
376 };
377 if (is_import) {
378 sym.value = 0;
379 sym.atom_ref = .{ .index = 0, .file = 0 };
380 sym.flags.weak = false;
381 sym.flags.weak_ref = nlist.weakRef();
382 sym.flags.import = is_import;
383 sym.visibility = .global;
384
385 const idx = self.globals.items[i];
386 macho_file.resolver.values.items[idx - 1] = .{ .index = @intCast(i), .file = self.index };
387 }
388 }
389}
390
365pub fn scanRelocs(self: *ZigObject, macho_file: *MachO) !void {391pub fn scanRelocs(self: *ZigObject, macho_file: *MachO) !void {
366 for (self.atoms.items) |atom_index| {392 for (self.getAtoms()) |atom_index| {
367 const atom = macho_file.getAtom(atom_index) orelse continue;393 const atom = self.getAtom(atom_index) orelse continue;
368 if (!atom.flags.alive) continue;394 if (!atom.flags.alive) continue;
369 const sect = atom.getInputSection(macho_file);395 const sect = atom.getInputSection(macho_file);
370 if (sect.isZerofill()) continue;396 if (sect.isZerofill()) continue;
...@@ -372,25 +398,168 @@ pub fn scanRelocs(self: *ZigObject, macho_file: *MachO) !void {...@@ -372,25 +398,168 @@ pub fn scanRelocs(self: *ZigObject, macho_file: *MachO) !void {
372 }398 }
373}399}
374400
375pub fn calcSymtabSize(self: *ZigObject, macho_file: *MachO) !void {401pub fn resolveRelocs(self: *ZigObject, macho_file: *MachO) !void {
402 const gpa = macho_file.base.comp.gpa;
403 var has_error = false;
404 for (self.getAtoms()) |atom_index| {
405 const atom = self.getAtom(atom_index) orelse continue;
406 if (!atom.flags.alive) continue;
407 const sect = &macho_file.sections.items(.header)[atom.out_n_sect];
408 if (sect.isZerofill()) continue;
409 if (!macho_file.isZigSection(atom.out_n_sect)) continue; // Non-Zig sections are handled separately
410 if (atom.getRelocs(macho_file).len == 0) continue;
411 // TODO: we will resolve and write ZigObject's TLS data twice:
412 // once here, and once in writeAtoms
413 const atom_size = std.math.cast(usize, atom.size) orelse return error.Overflow;
414 const code = try gpa.alloc(u8, atom_size);
415 defer gpa.free(code);
416 self.getAtomData(macho_file, atom.*, code) catch |err| {
417 switch (err) {
418 error.InputOutput => {
419 try macho_file.reportUnexpectedError("fetching code for '{s}' failed", .{
420 atom.getName(macho_file),
421 });
422 },
423 else => |e| {
424 try macho_file.reportUnexpectedError("unexpected error while fetching code for '{s}': {s}", .{
425 atom.getName(macho_file),
426 @errorName(e),
427 });
428 },
429 }
430 has_error = true;
431 continue;
432 };
433 const file_offset = sect.offset + atom.value;
434 atom.resolveRelocs(macho_file, code) catch |err| {
435 switch (err) {
436 error.ResolveFailed => {},
437 else => |e| {
438 try macho_file.reportUnexpectedError("unexpected error while resolving relocations: {s}", .{@errorName(e)});
439 },
440 }
441 has_error = true;
442 continue;
443 };
444 try macho_file.base.file.?.pwriteAll(code, file_offset);
445 }
446
447 if (has_error) return error.ResolveFailed;
448}
449
450pub fn calcNumRelocs(self: *ZigObject, macho_file: *MachO) void {
451 for (self.getAtoms()) |atom_index| {
452 const atom = self.getAtom(atom_index) orelse continue;
453 if (!atom.flags.alive) continue;
454 const header = &macho_file.sections.items(.header)[atom.out_n_sect];
455 if (header.isZerofill()) continue;
456 if (!macho_file.isZigSection(atom.out_n_sect) and !macho_file.isDebugSection(atom.out_n_sect)) continue;
457 const nreloc = atom.calcNumRelocs(macho_file);
458 atom.addExtra(.{ .rel_out_index = header.nreloc, .rel_out_count = nreloc }, macho_file);
459 header.nreloc += nreloc;
460 }
461}
462
463pub fn writeRelocs(self: *ZigObject, macho_file: *MachO) !void {
464 const gpa = macho_file.base.comp.gpa;
465
466 for (self.getAtoms()) |atom_index| {
467 const atom = self.getAtom(atom_index) orelse continue;
468 if (!atom.flags.alive) continue;
469 const header = macho_file.sections.items(.header)[atom.out_n_sect];
470 const relocs = macho_file.sections.items(.relocs)[atom.out_n_sect].items;
471 if (header.isZerofill()) continue;
472 if (!macho_file.isZigSection(atom.out_n_sect) and !macho_file.isDebugSection(atom.out_n_sect)) continue;
473 if (atom.getRelocs(macho_file).len == 0) continue;
474 const extra = atom.getExtra(macho_file);
475 const atom_size = std.math.cast(usize, atom.size) orelse return error.Overflow;
476 const code = try gpa.alloc(u8, atom_size);
477 defer gpa.free(code);
478 self.getAtomData(macho_file, atom.*, code) catch |err| switch (err) {
479 error.InputOutput => {
480 try macho_file.reportUnexpectedError("fetching code for '{s}' failed", .{
481 atom.getName(macho_file),
482 });
483 return error.FlushFailure;
484 },
485 else => |e| {
486 try macho_file.reportUnexpectedError("unexpected error while fetching code for '{s}': {s}", .{
487 atom.getName(macho_file),
488 @errorName(e),
489 });
490 return error.FlushFailure;
491 },
492 };
493 const file_offset = header.offset + atom.value;
494 try atom.writeRelocs(macho_file, code, relocs[extra.rel_out_index..][0..extra.rel_out_count]);
495 try macho_file.base.file.?.pwriteAll(code, file_offset);
496 }
497}
498
499// TODO we need this because not everything gets written out incrementally.
500// For example, TLS data gets written out via traditional route.
501// Is there any better way of handling this?
502pub fn writeAtomsRelocatable(self: *ZigObject, macho_file: *MachO) !void {
503 const tracy = trace(@src());
504 defer tracy.end();
505
506 for (self.getAtoms()) |atom_index| {
507 const atom = self.getAtom(atom_index) orelse continue;
508 if (!atom.flags.alive) continue;
509 const sect = atom.getInputSection(macho_file);
510 if (sect.isZerofill()) continue;
511 if (macho_file.isZigSection(atom.out_n_sect)) continue;
512 if (atom.getRelocs(macho_file).len == 0) continue;
513 const off = std.math.cast(usize, atom.value) orelse return error.Overflow;
514 const size = std.math.cast(usize, atom.size) orelse return error.Overflow;
515 const buffer = macho_file.sections.items(.out)[atom.out_n_sect].items;
516 try self.getAtomData(macho_file, atom.*, buffer[off..][0..size]);
517 const relocs = macho_file.sections.items(.relocs)[atom.out_n_sect].items;
518 const extra = atom.getExtra(macho_file);
519 try atom.writeRelocs(macho_file, buffer[off..][0..size], relocs[extra.rel_out_index..][0..extra.rel_out_count]);
520 }
521}
522
523// TODO we need this because not everything gets written out incrementally.
524// For example, TLS data gets written out via traditional route.
525// Is there any better way of handling this?
526pub fn writeAtoms(self: *ZigObject, macho_file: *MachO) !void {
527 const tracy = trace(@src());
528 defer tracy.end();
529
530 for (self.getAtoms()) |atom_index| {
531 const atom = self.getAtom(atom_index) orelse continue;
532 if (!atom.flags.alive) continue;
533 const sect = atom.getInputSection(macho_file);
534 if (sect.isZerofill()) continue;
535 if (macho_file.isZigSection(atom.out_n_sect)) continue;
536 const off = std.math.cast(usize, atom.value) orelse return error.Overflow;
537 const size = std.math.cast(usize, atom.size) orelse return error.Overflow;
538 const buffer = macho_file.sections.items(.out)[atom.out_n_sect].items;
539 try self.getAtomData(macho_file, atom.*, buffer[off..][0..size]);
540 try atom.resolveRelocs(macho_file, buffer[off..][0..size]);
541 }
542}
543
544pub fn calcSymtabSize(self: *ZigObject, macho_file: *MachO) void {
376 const tracy = trace(@src());545 const tracy = trace(@src());
377 defer tracy.end();546 defer tracy.end();
378547
379 for (self.symbols.items) |sym_index| {548 for (self.symbols.items, 0..) |*sym, i| {
380 const sym = macho_file.getSymbol(sym_index);549 const ref = self.getSymbolRef(@intCast(i), macho_file);
381 const file = sym.getFile(macho_file) orelse continue;550 const file = ref.getFile(macho_file) orelse continue;
382 if (file.getIndex() != self.index) continue;551 if (file.getIndex() != self.index) continue;
383 if (sym.getAtom(macho_file)) |atom| if (!atom.flags.alive) continue;552 if (sym.getAtom(macho_file)) |atom| if (!atom.flags.alive) continue;
384 sym.flags.output_symtab = true;553 sym.flags.output_symtab = true;
385 if (sym.isLocal()) {554 if (sym.isLocal()) {
386 try sym.addExtra(.{ .symtab = self.output_symtab_ctx.nlocals }, macho_file);555 sym.addExtra(.{ .symtab = self.output_symtab_ctx.nlocals }, macho_file);
387 self.output_symtab_ctx.nlocals += 1;556 self.output_symtab_ctx.nlocals += 1;
388 } else if (sym.flags.@"export") {557 } else if (sym.flags.@"export") {
389 try sym.addExtra(.{ .symtab = self.output_symtab_ctx.nexports }, macho_file);558 sym.addExtra(.{ .symtab = self.output_symtab_ctx.nexports }, macho_file);
390 self.output_symtab_ctx.nexports += 1;559 self.output_symtab_ctx.nexports += 1;
391 } else {560 } else {
392 assert(sym.flags.import);561 assert(sym.flags.import);
393 try sym.addExtra(.{ .symtab = self.output_symtab_ctx.nimports }, macho_file);562 sym.addExtra(.{ .symtab = self.output_symtab_ctx.nimports }, macho_file);
394 self.output_symtab_ctx.nimports += 1;563 self.output_symtab_ctx.nimports += 1;
395 }564 }
396 self.output_symtab_ctx.strsize += @as(u32, @intCast(sym.getName(macho_file).len + 1));565 self.output_symtab_ctx.strsize += @as(u32, @intCast(sym.getName(macho_file).len + 1));
...@@ -401,17 +570,20 @@ pub fn writeSymtab(self: ZigObject, macho_file: *MachO, ctx: anytype) void {...@@ -401,17 +570,20 @@ pub fn writeSymtab(self: ZigObject, macho_file: *MachO, ctx: anytype) void {
401 const tracy = trace(@src());570 const tracy = trace(@src());
402 defer tracy.end();571 defer tracy.end();
403572
404 for (self.symbols.items) |sym_index| {573 var n_strx = self.output_symtab_ctx.stroff;
405 const sym = macho_file.getSymbol(sym_index);574 for (self.symbols.items, 0..) |sym, i| {
406 const file = sym.getFile(macho_file) orelse continue;575 const ref = self.getSymbolRef(@intCast(i), macho_file);
576 const file = ref.getFile(macho_file) orelse continue;
407 if (file.getIndex() != self.index) continue;577 if (file.getIndex() != self.index) continue;
408 const idx = sym.getOutputSymtabIndex(macho_file) orelse continue;578 const idx = sym.getOutputSymtabIndex(macho_file) orelse continue;
409 const n_strx = @as(u32, @intCast(ctx.strtab.items.len));
410 ctx.strtab.appendSliceAssumeCapacity(sym.getName(macho_file));
411 ctx.strtab.appendAssumeCapacity(0);
412 const out_sym = &ctx.symtab.items[idx];579 const out_sym = &ctx.symtab.items[idx];
413 out_sym.n_strx = n_strx;580 out_sym.n_strx = n_strx;
414 sym.setOutputSym(macho_file, out_sym);581 sym.setOutputSym(macho_file, out_sym);
582 const name = sym.getName(macho_file);
583 @memcpy(ctx.strtab.items[n_strx..][0..name.len], name);
584 n_strx += @intCast(name.len);
585 ctx.strtab.items[n_strx] = 0;
586 n_strx += 1;
415 }587 }
416}588}
417589
...@@ -524,9 +696,9 @@ pub fn getDeclVAddr(...@@ -524,9 +696,9 @@ pub fn getDeclVAddr(
524 reloc_info: link.File.RelocInfo,696 reloc_info: link.File.RelocInfo,
525) !u64 {697) !u64 {
526 const sym_index = try self.getOrCreateMetadataForDecl(macho_file, decl_index);698 const sym_index = try self.getOrCreateMetadataForDecl(macho_file, decl_index);
527 const sym = macho_file.getSymbol(sym_index);699 const sym = self.symbols.items[sym_index];
528 const vaddr = sym.getAddress(.{}, macho_file);700 const vaddr = sym.getAddress(.{}, macho_file);
529 const parent_atom = macho_file.getSymbol(reloc_info.parent_atom_index).getAtom(macho_file).?;701 const parent_atom = self.symbols.items[reloc_info.parent_atom_index].getAtom(macho_file).?;
530 try parent_atom.addReloc(macho_file, .{702 try parent_atom.addReloc(macho_file, .{
531 .tag = .@"extern",703 .tag = .@"extern",
532 .offset = @intCast(reloc_info.offset),704 .offset = @intCast(reloc_info.offset),
...@@ -550,9 +722,9 @@ pub fn getAnonDeclVAddr(...@@ -550,9 +722,9 @@ pub fn getAnonDeclVAddr(
550 reloc_info: link.File.RelocInfo,722 reloc_info: link.File.RelocInfo,
551) !u64 {723) !u64 {
552 const sym_index = self.anon_decls.get(decl_val).?.symbol_index;724 const sym_index = self.anon_decls.get(decl_val).?.symbol_index;
553 const sym = macho_file.getSymbol(sym_index);725 const sym = self.symbols.items[sym_index];
554 const vaddr = sym.getAddress(.{}, macho_file);726 const vaddr = sym.getAddress(.{}, macho_file);
555 const parent_atom = macho_file.getSymbol(reloc_info.parent_atom_index).getAtom(macho_file).?;727 const parent_atom = self.symbols.items[reloc_info.parent_atom_index].getAtom(macho_file).?;
556 try parent_atom.addReloc(macho_file, .{728 try parent_atom.addReloc(macho_file, .{
557 .tag = .@"extern",729 .tag = .@"extern",
558 .offset = @intCast(reloc_info.offset),730 .offset = @intCast(reloc_info.offset),
...@@ -585,7 +757,7 @@ pub fn lowerAnonDecl(...@@ -585,7 +757,7 @@ pub fn lowerAnonDecl(
585 else => explicit_alignment,757 else => explicit_alignment,
586 };758 };
587 if (self.anon_decls.get(decl_val)) |metadata| {759 if (self.anon_decls.get(decl_val)) |metadata| {
588 const existing_alignment = macho_file.getSymbol(metadata.symbol_index).getAtom(macho_file).?.alignment;760 const existing_alignment = self.symbols.items[metadata.symbol_index].getAtom(macho_file).?.alignment;
589 if (decl_alignment.order(existing_alignment).compare(.lte))761 if (decl_alignment.order(existing_alignment).compare(.lte))
590 return .ok;762 return .ok;
591 }763 }
...@@ -629,13 +801,10 @@ fn freeUnnamedConsts(self: *ZigObject, macho_file: *MachO, decl_index: InternPoo...@@ -629,13 +801,10 @@ fn freeUnnamedConsts(self: *ZigObject, macho_file: *MachO, decl_index: InternPoo
629}801}
630802
631fn freeDeclMetadata(self: *ZigObject, macho_file: *MachO, sym_index: Symbol.Index) void {803fn freeDeclMetadata(self: *ZigObject, macho_file: *MachO, sym_index: Symbol.Index) void {
632 _ = self;804 const sym = self.symbols.items[sym_index];
633 const gpa = macho_file.base.comp.gpa;
634 const sym = macho_file.getSymbol(sym_index);
635 sym.getAtom(macho_file).?.free(macho_file);805 sym.getAtom(macho_file).?.free(macho_file);
636 log.debug("adding %{d} to local symbols free list", .{sym_index});806 log.debug("adding %{d} to local symbols free list", .{sym_index});
637 macho_file.symbols_free_list.append(gpa, sym_index) catch {};807 // TODO redo this
638 macho_file.symbols.items[sym_index] = .{};
639 // TODO free GOT entry here808 // TODO free GOT entry here
640}809}
641810
...@@ -676,7 +845,7 @@ pub fn updateFunc(...@@ -676,7 +845,7 @@ pub fn updateFunc(
676845
677 const sym_index = try self.getOrCreateMetadataForDecl(macho_file, decl_index);846 const sym_index = try self.getOrCreateMetadataForDecl(macho_file, decl_index);
678 self.freeUnnamedConsts(macho_file, decl_index);847 self.freeUnnamedConsts(macho_file, decl_index);
679 macho_file.getSymbol(sym_index).getAtom(macho_file).?.freeRelocs(macho_file);848 self.symbols.items[sym_index].getAtom(macho_file).?.freeRelocs(macho_file);
680849
681 var code_buffer = std.ArrayList(u8).init(gpa);850 var code_buffer = std.ArrayList(u8).init(gpa);
682 defer code_buffer.deinit();851 defer code_buffer.deinit();
...@@ -709,7 +878,7 @@ pub fn updateFunc(...@@ -709,7 +878,7 @@ pub fn updateFunc(
709 try self.updateDeclCode(macho_file, pt, decl_index, sym_index, sect_index, code);878 try self.updateDeclCode(macho_file, pt, decl_index, sym_index, sect_index, code);
710879
711 if (decl_state) |*ds| {880 if (decl_state) |*ds| {
712 const sym = macho_file.getSymbol(sym_index);881 const sym = self.symbols.items[sym_index];
713 try self.dwarf.?.commitDeclState(882 try self.dwarf.?.commitDeclState(
714 pt,883 pt,
715 decl_index,884 decl_index,
...@@ -744,13 +913,13 @@ pub fn updateDecl(...@@ -744,13 +913,13 @@ pub fn updateDecl(
744 const name = decl.name.toSlice(&mod.intern_pool);913 const name = decl.name.toSlice(&mod.intern_pool);
745 const lib_name = variable.lib_name.toSlice(&mod.intern_pool);914 const lib_name = variable.lib_name.toSlice(&mod.intern_pool);
746 const index = try self.getGlobalSymbol(macho_file, name, lib_name);915 const index = try self.getGlobalSymbol(macho_file, name, lib_name);
747 const actual_index = self.symbols.items[index];916 const sym = &self.symbols.items[index];
748 macho_file.getSymbol(actual_index).flags.needs_got = true;917 sym.flags.needs_got = true;
749 return;918 return;
750 }919 }
751920
752 const sym_index = try self.getOrCreateMetadataForDecl(macho_file, decl_index);921 const sym_index = try self.getOrCreateMetadataForDecl(macho_file, decl_index);
753 macho_file.getSymbol(sym_index).getAtom(macho_file).?.freeRelocs(macho_file);922 self.symbols.items[sym_index].getAtom(macho_file).?.freeRelocs(macho_file);
754923
755 const gpa = macho_file.base.comp.gpa;924 const gpa = macho_file.base.comp.gpa;
756 var code_buffer = std.ArrayList(u8).init(gpa);925 var code_buffer = std.ArrayList(u8).init(gpa);
...@@ -773,19 +942,16 @@ pub fn updateDecl(...@@ -773,19 +942,16 @@ pub fn updateDecl(
773 return;942 return;
774 },943 },
775 };944 };
776 const sect_index = try self.getDeclOutputSection(macho_file, decl, code);945 if (isThreadlocal(macho_file, decl_index)) {
777 const is_threadlocal = switch (macho_file.sections.items(.header)[sect_index].type()) {946 const sect_index = try self.getDeclOutputSection(macho_file, decl, code);
778 macho.S_THREAD_LOCAL_ZEROFILL, macho.S_THREAD_LOCAL_REGULAR => true,
779 else => false,
780 };
781 if (is_threadlocal) {
782 try self.updateTlv(macho_file, pt, decl_index, sym_index, sect_index, code);947 try self.updateTlv(macho_file, pt, decl_index, sym_index, sect_index, code);
783 } else {948 } else {
949 const sect_index = try self.getDeclOutputSection(macho_file, decl, code);
784 try self.updateDeclCode(macho_file, pt, decl_index, sym_index, sect_index, code);950 try self.updateDeclCode(macho_file, pt, decl_index, sym_index, sect_index, code);
785 }951 }
786952
787 if (decl_state) |*ds| {953 if (decl_state) |*ds| {
788 const sym = macho_file.getSymbol(sym_index);954 const sym = self.symbols.items[sym_index];
789 try self.dwarf.?.commitDeclState(955 try self.dwarf.?.commitDeclState(
790 pt,956 pt,
791 decl_index,957 decl_index,
...@@ -817,14 +983,16 @@ fn updateDeclCode(...@@ -817,14 +983,16 @@ fn updateDeclCode(
817 const required_alignment = decl.getAlignment(pt);983 const required_alignment = decl.getAlignment(pt);
818984
819 const sect = &macho_file.sections.items(.header)[sect_index];985 const sect = &macho_file.sections.items(.header)[sect_index];
820 const sym = macho_file.getSymbol(sym_index);986 const sym = &self.symbols.items[sym_index];
821 const nlist = &self.symtab.items(.nlist)[sym.nlist_idx];987 const nlist = &self.symtab.items(.nlist)[sym.nlist_idx];
822 const atom = sym.getAtom(macho_file).?;988 const atom = sym.getAtom(macho_file).?;
823989
824 sym.out_n_sect = sect_index;990 sym.out_n_sect = sect_index;
825 atom.out_n_sect = sect_index;991 atom.out_n_sect = sect_index;
826992
827 sym.name = try self.strtab.insert(gpa, decl.fqn.toSlice(ip));993 const sym_name = try std.fmt.allocPrintZ(gpa, "_{s}", .{decl.fqn.toSlice(ip)});
994 defer gpa.free(sym_name);
995 sym.name = try self.strtab.insert(gpa, sym_name);
828 atom.flags.alive = true;996 atom.flags.alive = true;
829 atom.name = sym.name;997 atom.name = sym.name;
830 nlist.n_strx = sym.name;998 nlist.n_strx = sym.name;
...@@ -851,13 +1019,13 @@ fn updateDeclCode(...@@ -851,13 +1019,13 @@ fn updateDeclCode(
851 if (!macho_file.base.isRelocatable()) {1019 if (!macho_file.base.isRelocatable()) {
852 log.debug(" (updating offset table entry)", .{});1020 log.debug(" (updating offset table entry)", .{});
853 assert(sym.flags.has_zig_got);1021 assert(sym.flags.has_zig_got);
854 const extra = sym.getExtra(macho_file).?;1022 const extra = sym.getExtra(macho_file);
855 try macho_file.zig_got.writeOne(macho_file, extra.zig_got);1023 try macho_file.zig_got.writeOne(macho_file, extra.zig_got);
856 }1024 }
857 }1025 }
858 } else if (code.len < old_size) {1026 } else if (code.len < old_size) {
859 atom.shrink(macho_file);1027 atom.shrink(macho_file);
860 } else if (macho_file.getAtom(atom.next_index) == null) {1028 } else if (self.getAtom(atom.next_index) == null) {
861 const needed_size = atom.value + code.len;1029 const needed_size = atom.value + code.len;
862 sect.size = needed_size;1030 sect.size = needed_size;
863 }1031 }
...@@ -922,31 +1090,22 @@ fn createTlvInitializer(...@@ -922,31 +1090,22 @@ fn createTlvInitializer(
922 const gpa = macho_file.base.comp.gpa;1090 const gpa = macho_file.base.comp.gpa;
923 const sym_name = try std.fmt.allocPrint(gpa, "{s}$tlv$init", .{name});1091 const sym_name = try std.fmt.allocPrint(gpa, "{s}$tlv$init", .{name});
924 defer gpa.free(sym_name);1092 defer gpa.free(sym_name);
1093 const off = try self.strtab.insert(gpa, sym_name);
9251094
926 const sym_index = try self.addAtom(macho_file);1095 const sym_index = try self.newSymbolWithAtom(gpa, off, macho_file);
927 const sym = macho_file.getSymbol(sym_index);1096 const sym = &self.symbols.items[sym_index];
928 const nlist = &self.symtab.items(.nlist)[sym.nlist_idx];1097 const nlist = &self.symtab.items(.nlist)[sym.nlist_idx];
929 const atom = sym.getAtom(macho_file).?;1098 const atom = sym.getAtom(macho_file).?;
930
931 sym.out_n_sect = sect_index;1099 sym.out_n_sect = sect_index;
932 atom.out_n_sect = sect_index;1100 atom.out_n_sect = sect_index;
933
934 sym.value = 0;
935 sym.name = try self.strtab.insert(gpa, sym_name);
936 atom.flags.alive = true;1101 atom.flags.alive = true;
937 atom.name = sym.name;
938 nlist.n_strx = sym.name;
939 nlist.n_sect = sect_index + 1;
940 nlist.n_type = macho.N_SECT;
941 nlist.n_value = 0;
942 self.symtab.items(.size)[sym.nlist_idx] = code.len;
943
944 atom.alignment = alignment;1102 atom.alignment = alignment;
945 atom.size = code.len;1103 atom.size = code.len;
1104 nlist.n_sect = sect_index + 1;
1105 self.symtab.items(.size)[sym.nlist_idx] = code.len;
9461106
947 const slice = macho_file.sections.slice();1107 const slice = macho_file.sections.slice();
948 const header = slice.items(.header)[sect_index];1108 const header = slice.items(.header)[sect_index];
949 const atoms = &slice.items(.atoms)[sect_index];
9501109
951 const gop = try self.tlv_initializers.getOrPut(gpa, atom.atom_index);1110 const gop = try self.tlv_initializers.getOrPut(gpa, atom.atom_index);
952 assert(!gop.found_existing); // TODO incremental updates1111 assert(!gop.found_existing); // TODO incremental updates
...@@ -957,8 +1116,6 @@ fn createTlvInitializer(...@@ -957,8 +1116,6 @@ fn createTlvInitializer(
957 gop.value_ptr.data = try gpa.dupe(u8, code);1116 gop.value_ptr.data = try gpa.dupe(u8, code);
958 }1117 }
9591118
960 try atoms.append(gpa, atom.atom_index);
961
962 return sym_index;1119 return sym_index;
963}1120}
9641121
...@@ -971,7 +1128,7 @@ fn createTlvDescriptor(...@@ -971,7 +1128,7 @@ fn createTlvDescriptor(
971) !void {1128) !void {
972 const gpa = macho_file.base.comp.gpa;1129 const gpa = macho_file.base.comp.gpa;
9731130
974 const sym = macho_file.getSymbol(sym_index);1131 const sym = &self.symbols.items[sym_index];
975 const nlist = &self.symtab.items(.nlist)[sym.nlist_idx];1132 const nlist = &self.symtab.items(.nlist)[sym.nlist_idx];
976 const atom = sym.getAtom(macho_file).?;1133 const atom = sym.getAtom(macho_file).?;
977 const alignment = Atom.Alignment.fromNonzeroByteUnits(@alignOf(u64));1134 const alignment = Atom.Alignment.fromNonzeroByteUnits(@alignOf(u64));
...@@ -1001,7 +1158,7 @@ fn createTlvDescriptor(...@@ -1001,7 +1158,7 @@ fn createTlvDescriptor(
1001 try atom.addReloc(macho_file, .{1158 try atom.addReloc(macho_file, .{
1002 .tag = .@"extern",1159 .tag = .@"extern",
1003 .offset = 0,1160 .offset = 0,
1004 .target = self.symbols.items[tlv_bootstrap_index],1161 .target = tlv_bootstrap_index,
1005 .addend = 0,1162 .addend = 0,
1006 .type = .unsigned,1163 .type = .unsigned,
1007 .meta = .{1164 .meta = .{
...@@ -1021,11 +1178,9 @@ fn createTlvDescriptor(...@@ -1021,11 +1178,9 @@ fn createTlvDescriptor(
1021 .pcrel = false,1178 .pcrel = false,
1022 .has_subtractor = false,1179 .has_subtractor = false,
1023 .length = 3,1180 .length = 3,
1024 .symbolnum = @intCast(macho_file.getSymbol(init_sym_index).nlist_idx),1181 .symbolnum = @intCast(init_sym_index),
1025 },1182 },
1026 });1183 });
1027
1028 try macho_file.sections.items(.atoms)[sect_index].append(gpa, atom.atom_index);
1029}1184}
10301185
1031fn getDeclOutputSection(1186fn getDeclOutputSection(
...@@ -1116,8 +1271,8 @@ pub fn lowerUnnamedConst(...@@ -1116,8 +1271,8 @@ pub fn lowerUnnamedConst(
1116 return error.CodegenFail;1271 return error.CodegenFail;
1117 },1272 },
1118 };1273 };
1119 const sym = macho_file.getSymbol(sym_index);1274 const sym = self.symbols.items[sym_index];
1120 try unnamed_consts.append(gpa, sym.atom);1275 try unnamed_consts.append(gpa, sym.atom_ref.index);
1121 return sym_index;1276 return sym_index;
1122}1277}
11231278
...@@ -1141,7 +1296,8 @@ fn lowerConst(...@@ -1141,7 +1296,8 @@ fn lowerConst(
1141 var code_buffer = std.ArrayList(u8).init(gpa);1296 var code_buffer = std.ArrayList(u8).init(gpa);
1142 defer code_buffer.deinit();1297 defer code_buffer.deinit();
11431298
1144 const sym_index = try self.addAtom(macho_file);1299 const name_str_index = try self.strtab.insert(gpa, name);
1300 const sym_index = try self.newSymbolWithAtom(gpa, name_str_index, macho_file);
11451301
1146 const res = try codegen.generateSymbol(&macho_file.base, pt, src_loc, val, &code_buffer, .{1302 const res = try codegen.generateSymbol(&macho_file.base, pt, src_loc, val, &code_buffer, .{
1147 .none = {},1303 .none = {},
...@@ -1153,20 +1309,15 @@ fn lowerConst(...@@ -1153,20 +1309,15 @@ fn lowerConst(
1153 .fail => |em| return .{ .fail = em },1309 .fail => |em| return .{ .fail = em },
1154 };1310 };
11551311
1156 const sym = macho_file.getSymbol(sym_index);1312 const sym = &self.symbols.items[sym_index];
1157 const name_str_index = try self.strtab.insert(gpa, name);
1158 sym.name = name_str_index;
1159 sym.out_n_sect = output_section_index;1313 sym.out_n_sect = output_section_index;
11601314
1161 const nlist = &self.symtab.items(.nlist)[sym.nlist_idx];1315 const nlist = &self.symtab.items(.nlist)[sym.nlist_idx];
1162 nlist.n_strx = name_str_index;
1163 nlist.n_type = macho.N_SECT;
1164 nlist.n_sect = output_section_index + 1;1316 nlist.n_sect = output_section_index + 1;
1165 self.symtab.items(.size)[sym.nlist_idx] = code.len;1317 self.symtab.items(.size)[sym.nlist_idx] = code.len;
11661318
1167 const atom = sym.getAtom(macho_file).?;1319 const atom = sym.getAtom(macho_file).?;
1168 atom.flags.alive = true;1320 atom.flags.alive = true;
1169 atom.name = name_str_index;
1170 atom.alignment = required_alignment;1321 atom.alignment = required_alignment;
1171 atom.size = code.len;1322 atom.size = code.len;
1172 atom.out_n_sect = output_section_index;1323 atom.out_n_sect = output_section_index;
...@@ -1175,9 +1326,6 @@ fn lowerConst(...@@ -1175,9 +1326,6 @@ fn lowerConst(
1175 // TODO rename and re-audit this method1326 // TODO rename and re-audit this method
1176 errdefer self.freeDeclMetadata(macho_file, sym_index);1327 errdefer self.freeDeclMetadata(macho_file, sym_index);
11771328
1178 sym.value = 0;
1179 nlist.n_value = 0;
1180
1181 const sect = macho_file.sections.items(.header)[output_section_index];1329 const sect = macho_file.sections.items(.header)[output_section_index];
1182 const file_offset = sect.offset + atom.value;1330 const file_offset = sect.offset + atom.value;
1183 try macho_file.base.file.?.pwriteAll(code, file_offset);1331 try macho_file.base.file.?.pwriteAll(code, file_offset);
...@@ -1219,7 +1367,7 @@ pub fn updateExports(...@@ -1219,7 +1367,7 @@ pub fn updateExports(
1219 },1367 },
1220 };1368 };
1221 const sym_index = metadata.symbol_index;1369 const sym_index = metadata.symbol_index;
1222 const nlist_idx = macho_file.getSymbol(sym_index).nlist_idx;1370 const nlist_idx = self.symbols.items[sym_index].nlist_idx;
1223 const nlist = self.symtab.items(.nlist)[nlist_idx];1371 const nlist = self.symtab.items(.nlist)[nlist_idx];
12241372
1225 for (export_indices) |export_idx| {1373 for (export_indices) |export_idx| {
...@@ -1255,22 +1403,30 @@ pub fn updateExports(...@@ -1255,22 +1403,30 @@ pub fn updateExports(
1255 break :blk global_nlist_index;1403 break :blk global_nlist_index;
1256 };1404 };
1257 const global_nlist = &self.symtab.items(.nlist)[global_nlist_index];1405 const global_nlist = &self.symtab.items(.nlist)[global_nlist_index];
1406 const atom_index = self.symtab.items(.atom)[nlist_idx];
1407 const global_sym = &self.symbols.items[global_nlist_index];
1258 global_nlist.n_value = nlist.n_value;1408 global_nlist.n_value = nlist.n_value;
1259 global_nlist.n_sect = nlist.n_sect;1409 global_nlist.n_sect = nlist.n_sect;
1260 global_nlist.n_type = macho.N_EXT | macho.N_SECT;1410 global_nlist.n_type = macho.N_EXT | macho.N_SECT;
1261 self.symtab.items(.size)[global_nlist_index] = self.symtab.items(.size)[nlist_idx];1411 self.symtab.items(.size)[global_nlist_index] = self.symtab.items(.size)[nlist_idx];
1262 self.symtab.items(.atom)[global_nlist_index] = self.symtab.items(.atom)[nlist_idx];1412 self.symtab.items(.atom)[global_nlist_index] = atom_index;
1413 global_sym.atom_ref = .{ .index = atom_index, .file = self.index };
12631414
1264 switch (exp.opts.linkage) {1415 switch (exp.opts.linkage) {
1265 .internal => {1416 .internal => {
1266 // Symbol should be hidden, or in MachO lingo, private extern.1417 // Symbol should be hidden, or in MachO lingo, private extern.
1267 global_nlist.n_type |= macho.N_PEXT;1418 global_nlist.n_type |= macho.N_PEXT;
1419 global_sym.visibility = .hidden;
1420 },
1421 .strong => {
1422 global_sym.visibility = .global;
1268 },1423 },
1269 .strong => {},
1270 .weak => {1424 .weak => {
1271 // Weak linkage is specified as part of n_desc field.1425 // Weak linkage is specified as part of n_desc field.
1272 // Symbol's n_type is like for a symbol with strong linkage.1426 // Symbol's n_type is like for a symbol with strong linkage.
1273 global_nlist.n_desc |= macho.N_WEAK_DEF;1427 global_nlist.n_desc |= macho.N_WEAK_DEF;
1428 global_sym.visibility = .global;
1429 global_sym.flags.weak = true;
1274 },1430 },
1275 else => unreachable,1431 else => unreachable,
1276 }1432 }
...@@ -1323,7 +1479,7 @@ fn updateLazySymbol(...@@ -1323,7 +1479,7 @@ fn updateLazySymbol(
1323 .code => macho_file.zig_text_sect_index.?,1479 .code => macho_file.zig_text_sect_index.?,
1324 .const_data => macho_file.zig_const_sect_index.?,1480 .const_data => macho_file.zig_const_sect_index.?,
1325 };1481 };
1326 const sym = macho_file.getSymbol(symbol_index);1482 const sym = &self.symbols.items[symbol_index];
1327 sym.name = name_str_index;1483 sym.name = name_str_index;
1328 sym.out_n_sect = output_section_index;1484 sym.out_n_sect = output_section_index;
13291485
...@@ -1383,12 +1539,12 @@ pub fn deleteExport(...@@ -1383,12 +1539,12 @@ pub fn deleteExport(
1383 const nlist = &self.symtab.items(.nlist)[nlist_index.*];1539 const nlist = &self.symtab.items(.nlist)[nlist_index.*];
1384 self.symtab.items(.size)[nlist_index.*] = 0;1540 self.symtab.items(.size)[nlist_index.*] = 0;
1385 _ = self.globals_lookup.remove(nlist.n_strx);1541 _ = self.globals_lookup.remove(nlist.n_strx);
1386 const sym_index = macho_file.globals.get(nlist.n_strx).?;1542 // TODO actually remove the export
1387 const sym = macho_file.getSymbol(sym_index);1543 // const sym_index = macho_file.globals.get(nlist.n_strx).?;
1388 if (sym.file == self.index) {1544 // const sym = &self.symbols.items[sym_index];
1389 _ = macho_file.globals.swapRemove(nlist.n_strx);1545 // if (sym.file == self.index) {
1390 sym.* = .{};1546 // sym.* = .{};
1391 }1547 // }
1392 nlist.* = MachO.null_sym;1548 nlist.* = MachO.null_sym;
1393}1549}
13941550
...@@ -1400,14 +1556,9 @@ pub fn getGlobalSymbol(self: *ZigObject, macho_file: *MachO, name: []const u8, l...@@ -1400,14 +1556,9 @@ pub fn getGlobalSymbol(self: *ZigObject, macho_file: *MachO, name: []const u8, l
1400 const off = try self.strtab.insert(gpa, sym_name);1556 const off = try self.strtab.insert(gpa, sym_name);
1401 const lookup_gop = try self.globals_lookup.getOrPut(gpa, off);1557 const lookup_gop = try self.globals_lookup.getOrPut(gpa, off);
1402 if (!lookup_gop.found_existing) {1558 if (!lookup_gop.found_existing) {
1403 const nlist_index = try self.addNlist(gpa);1559 const sym_index = try self.newSymbol(gpa, off, .{});
1404 const nlist = &self.symtab.items(.nlist)[nlist_index];1560 const sym = &self.symbols.items[sym_index];
1405 nlist.n_strx = off;1561 lookup_gop.value_ptr.* = sym.nlist_idx;
1406 nlist.n_type = macho.N_EXT;
1407 lookup_gop.value_ptr.* = nlist_index;
1408 const global_name_off = try macho_file.strings.insert(gpa, sym_name);
1409 const gop = try macho_file.getOrCreateGlobal(global_name_off);
1410 try self.symbols.append(gpa, gop.index);
1411 }1562 }
1412 return lookup_gop.value_ptr.*;1563 return lookup_gop.value_ptr.*;
1413}1564}
...@@ -1420,17 +1571,11 @@ pub fn getOrCreateMetadataForDecl(...@@ -1420,17 +1571,11 @@ pub fn getOrCreateMetadataForDecl(
1420 const gpa = macho_file.base.comp.gpa;1571 const gpa = macho_file.base.comp.gpa;
1421 const gop = try self.decls.getOrPut(gpa, decl_index);1572 const gop = try self.decls.getOrPut(gpa, decl_index);
1422 if (!gop.found_existing) {1573 if (!gop.found_existing) {
1423 const any_non_single_threaded = macho_file.base.comp.config.any_non_single_threaded;1574 const sym_index = try self.newSymbolWithAtom(gpa, 0, macho_file);
1424 const sym_index = try self.addAtom(macho_file);1575 const sym = &self.symbols.items[sym_index];
1425 const mod = macho_file.base.comp.module.?;1576 if (isThreadlocal(macho_file, decl_index)) {
1426 const decl = mod.declPtr(decl_index);1577 sym.flags.tlv = true;
1427 const sym = macho_file.getSymbol(sym_index);1578 } else {
1428 if (decl.getOwnedVariable(mod)) |variable| {
1429 if (variable.is_threadlocal and any_non_single_threaded) {
1430 sym.flags.tlv = true;
1431 }
1432 }
1433 if (!sym.flags.tlv) {
1434 sym.flags.needs_zig_got = true;1579 sym.flags.needs_zig_got = true;
1435 }1580 }
1436 gop.value_ptr.* = .{ .symbol_index = sym_index };1581 gop.value_ptr.* = .{ .symbol_index = sym_index };
...@@ -1464,8 +1609,8 @@ pub fn getOrCreateMetadataForLazySymbol(...@@ -1464,8 +1609,8 @@ pub fn getOrCreateMetadataForLazySymbol(
1464 };1609 };
1465 switch (metadata.state.*) {1610 switch (metadata.state.*) {
1466 .unused => {1611 .unused => {
1467 const symbol_index = try self.addAtom(macho_file);1612 const symbol_index = try self.newSymbolWithAtom(gpa, 0, macho_file);
1468 const sym = macho_file.getSymbol(symbol_index);1613 const sym = &self.symbols.items[symbol_index];
1469 sym.flags.needs_zig_got = true;1614 sym.flags.needs_zig_got = true;
1470 metadata.symbol_index.* = symbol_index;1615 metadata.symbol_index.* = symbol_index;
1471 },1616 },
...@@ -1479,6 +1624,144 @@ pub fn getOrCreateMetadataForLazySymbol(...@@ -1479,6 +1624,144 @@ pub fn getOrCreateMetadataForLazySymbol(
1479 return symbol_index;1624 return symbol_index;
1480}1625}
14811626
1627fn isThreadlocal(macho_file: *MachO, decl_index: InternPool.DeclIndex) bool {
1628 const any_non_single_threaded = macho_file.base.comp.config.any_non_single_threaded;
1629 const zcu = macho_file.base.comp.module.?;
1630 const decl = zcu.declPtr(decl_index);
1631 const variable = decl.getOwnedVariable(zcu) orelse return false;
1632 return variable.is_threadlocal and any_non_single_threaded;
1633}
1634
1635fn addAtom(self: *ZigObject, allocator: Allocator) !Atom.Index {
1636 try self.atoms.ensureUnusedCapacity(allocator, 1);
1637 try self.atoms_extra.ensureUnusedCapacity(allocator, 1);
1638 return self.addAtomAssumeCapacity();
1639}
1640
1641fn addAtomAssumeCapacity(self: *ZigObject) Atom.Index {
1642 const atom_index: Atom.Index = @intCast(self.atoms.items.len);
1643 const atom = self.atoms.addOneAssumeCapacity();
1644 atom.* = .{
1645 .file = self.index,
1646 .atom_index = atom_index,
1647 .extra = self.addAtomExtraAssumeCapacity(.{}),
1648 };
1649 return atom_index;
1650}
1651
1652pub fn getAtom(self: *ZigObject, atom_index: Atom.Index) ?*Atom {
1653 if (atom_index == 0) return null;
1654 assert(atom_index < self.atoms.items.len);
1655 return &self.atoms.items[atom_index];
1656}
1657
1658pub fn getAtoms(self: *ZigObject) []const Atom.Index {
1659 return self.atoms_indexes.items;
1660}
1661
1662fn addAtomExtra(self: *ZigObject, allocator: Allocator, extra: Atom.Extra) !u32 {
1663 const fields = @typeInfo(Atom.Extra).Struct.fields;
1664 try self.atoms_extra.ensureUnusedCapacity(allocator, fields.len);
1665 return self.addAtomExtraAssumeCapacity(extra);
1666}
1667
1668fn addAtomExtraAssumeCapacity(self: *ZigObject, extra: Atom.Extra) u32 {
1669 const index = @as(u32, @intCast(self.atoms_extra.items.len));
1670 const fields = @typeInfo(Atom.Extra).Struct.fields;
1671 inline for (fields) |field| {
1672 self.atoms_extra.appendAssumeCapacity(switch (field.type) {
1673 u32 => @field(extra, field.name),
1674 else => @compileError("bad field type"),
1675 });
1676 }
1677 return index;
1678}
1679
1680pub fn getAtomExtra(self: ZigObject, index: u32) Atom.Extra {
1681 const fields = @typeInfo(Atom.Extra).Struct.fields;
1682 var i: usize = index;
1683 var result: Atom.Extra = undefined;
1684 inline for (fields) |field| {
1685 @field(result, field.name) = switch (field.type) {
1686 u32 => self.atoms_extra.items[i],
1687 else => @compileError("bad field type"),
1688 };
1689 i += 1;
1690 }
1691 return result;
1692}
1693
1694pub fn setAtomExtra(self: *ZigObject, index: u32, extra: Atom.Extra) void {
1695 assert(index > 0);
1696 const fields = @typeInfo(Atom.Extra).Struct.fields;
1697 inline for (fields, 0..) |field, i| {
1698 self.atoms_extra.items[index + i] = switch (field.type) {
1699 u32 => @field(extra, field.name),
1700 else => @compileError("bad field type"),
1701 };
1702 }
1703}
1704
1705fn addSymbol(self: *ZigObject, allocator: Allocator) !Symbol.Index {
1706 try self.symbols.ensureUnusedCapacity(allocator, 1);
1707 return self.addSymbolAssumeCapacity();
1708}
1709
1710fn addSymbolAssumeCapacity(self: *ZigObject) Symbol.Index {
1711 const index: Symbol.Index = @intCast(self.symbols.items.len);
1712 const symbol = self.symbols.addOneAssumeCapacity();
1713 symbol.* = .{ .file = self.index };
1714 return index;
1715}
1716
1717pub fn getSymbolRef(self: ZigObject, index: Symbol.Index, macho_file: *MachO) MachO.Ref {
1718 const global_index = self.globals.items[index];
1719 if (macho_file.resolver.get(global_index)) |ref| return ref;
1720 return .{ .index = index, .file = self.index };
1721}
1722
1723pub fn addSymbolExtra(self: *ZigObject, allocator: Allocator, extra: Symbol.Extra) !u32 {
1724 const fields = @typeInfo(Symbol.Extra).Struct.fields;
1725 try self.symbols_extra.ensureUnusedCapacity(allocator, fields.len);
1726 return self.addSymbolExtraAssumeCapacity(extra);
1727}
1728
1729fn addSymbolExtraAssumeCapacity(self: *ZigObject, extra: Symbol.Extra) u32 {
1730 const index = @as(u32, @intCast(self.symbols_extra.items.len));
1731 const fields = @typeInfo(Symbol.Extra).Struct.fields;
1732 inline for (fields) |field| {
1733 self.symbols_extra.appendAssumeCapacity(switch (field.type) {
1734 u32 => @field(extra, field.name),
1735 else => @compileError("bad field type"),
1736 });
1737 }
1738 return index;
1739}
1740
1741pub fn getSymbolExtra(self: ZigObject, index: u32) Symbol.Extra {
1742 const fields = @typeInfo(Symbol.Extra).Struct.fields;
1743 var i: usize = index;
1744 var result: Symbol.Extra = undefined;
1745 inline for (fields) |field| {
1746 @field(result, field.name) = switch (field.type) {
1747 u32 => self.symbols_extra.items[i],
1748 else => @compileError("bad field type"),
1749 };
1750 i += 1;
1751 }
1752 return result;
1753}
1754
1755pub fn setSymbolExtra(self: *ZigObject, index: u32, extra: Symbol.Extra) void {
1756 const fields = @typeInfo(Symbol.Extra).Struct.fields;
1757 inline for (fields, 0..) |field, i| {
1758 self.symbols_extra.items[index + i] = switch (field.type) {
1759 u32 => @field(extra, field.name),
1760 else => @compileError("bad field type"),
1761 };
1762 }
1763}
1764
1482pub fn asFile(self: *ZigObject) File {1765pub fn asFile(self: *ZigObject) File {
1483 return .{ .zig_object = self };1766 return .{ .zig_object = self };
1484}1767}
...@@ -1504,9 +1787,16 @@ fn formatSymtab(...@@ -1504,9 +1787,16 @@ fn formatSymtab(
1504 _ = unused_fmt_string;1787 _ = unused_fmt_string;
1505 _ = options;1788 _ = options;
1506 try writer.writeAll(" symbols\n");1789 try writer.writeAll(" symbols\n");
1507 for (ctx.self.symbols.items) |index| {1790 const self = ctx.self;
1508 const sym = ctx.macho_file.getSymbol(index);1791 const macho_file = ctx.macho_file;
1509 try writer.print(" {}\n", .{sym.fmt(ctx.macho_file)});1792 for (self.symbols.items, 0..) |sym, i| {
1793 const ref = self.getSymbolRef(@intCast(i), macho_file);
1794 if (ref.getFile(macho_file) == null) {
1795 // TODO any better way of handling this?
1796 try writer.print(" {s} : unclaimed\n", .{sym.getName(macho_file)});
1797 } else {
1798 try writer.print(" {}\n", .{ref.getSymbol(macho_file).?.fmt(macho_file)});
1799 }
1510 }1800 }
1511}1801}
15121802
...@@ -1525,10 +1815,12 @@ fn formatAtoms(...@@ -1525,10 +1815,12 @@ fn formatAtoms(
1525) !void {1815) !void {
1526 _ = unused_fmt_string;1816 _ = unused_fmt_string;
1527 _ = options;1817 _ = options;
1818 const self = ctx.self;
1819 const macho_file = ctx.macho_file;
1528 try writer.writeAll(" atoms\n");1820 try writer.writeAll(" atoms\n");
1529 for (ctx.self.atoms.items) |atom_index| {1821 for (self.getAtoms()) |atom_index| {
1530 const atom = ctx.macho_file.getAtom(atom_index) orelse continue;1822 const atom = self.getAtom(atom_index) orelse continue;
1531 try writer.print(" {}\n", .{atom.fmt(ctx.macho_file)});1823 try writer.print(" {}\n", .{atom.fmt(macho_file)});
1532 }1824 }
1533}1825}
15341826
src/link/MachO/dead_strip.zig+45-27
...@@ -17,16 +17,16 @@ pub fn gcAtoms(macho_file: *MachO) !void {...@@ -17,16 +17,16 @@ pub fn gcAtoms(macho_file: *MachO) !void {
17fn collectRoots(roots: *std.ArrayList(*Atom), objects: []const File.Index, macho_file: *MachO) !void {17fn collectRoots(roots: *std.ArrayList(*Atom), objects: []const File.Index, macho_file: *MachO) !void {
18 for (objects) |index| {18 for (objects) |index| {
19 const object = macho_file.getFile(index).?;19 const object = macho_file.getFile(index).?;
20 for (object.getSymbols()) |sym_index| {20 for (object.getSymbols(), 0..) |*sym, i| {
21 const sym = macho_file.getSymbol(sym_index);21 const ref = object.getSymbolRef(@intCast(i), macho_file);
22 const file = sym.getFile(macho_file) orelse continue;22 const file = ref.getFile(macho_file) orelse continue;
23 if (file.getIndex() != index) continue;23 if (file.getIndex() != index) continue;
24 if (sym.flags.no_dead_strip or (macho_file.base.isDynLib() and sym.visibility == .global))24 if (sym.flags.no_dead_strip or (macho_file.base.isDynLib() and sym.visibility == .global))
25 try markSymbol(sym, roots, macho_file);25 try markSymbol(sym, roots, macho_file);
26 }26 }
2727
28 for (object.getAtoms()) |atom_index| {28 for (object.getAtoms()) |atom_index| {
29 const atom = macho_file.getAtom(atom_index).?;29 const atom = object.getAtom(atom_index) orelse continue;
30 const isec = atom.getInputSection(macho_file);30 const isec = atom.getInputSection(macho_file);
31 switch (isec.type()) {31 switch (isec.type()) {
32 macho.S_MOD_INIT_FUNC_POINTERS,32 macho.S_MOD_INIT_FUNC_POINTERS,
...@@ -41,8 +41,9 @@ fn collectRoots(roots: *std.ArrayList(*Atom), objects: []const File.Index, macho...@@ -41,8 +41,9 @@ fn collectRoots(roots: *std.ArrayList(*Atom), objects: []const File.Index, macho
41 }41 }
4242
43 for (macho_file.objects.items) |index| {43 for (macho_file.objects.items) |index| {
44 for (macho_file.getFile(index).?.object.unwind_records.items) |cu_index| {44 const object = macho_file.getFile(index).?.object;
45 const cu = macho_file.getUnwindRecord(cu_index);45 for (object.unwind_records_indexes.items) |cu_index| {
46 const cu = object.getUnwindRecord(cu_index);
46 if (!cu.alive) continue;47 if (!cu.alive) continue;
47 if (cu.getFde(macho_file)) |fde| {48 if (cu.getFde(macho_file)) |fde| {
48 if (fde.getCie(macho_file).getPersonality(macho_file)) |sym| try markSymbol(sym, roots, macho_file);49 if (fde.getCie(macho_file).getPersonality(macho_file)) |sym| try markSymbol(sym, roots, macho_file);
...@@ -50,19 +51,27 @@ fn collectRoots(roots: *std.ArrayList(*Atom), objects: []const File.Index, macho...@@ -50,19 +51,27 @@ fn collectRoots(roots: *std.ArrayList(*Atom), objects: []const File.Index, macho
50 }51 }
51 }52 }
5253
53 for (macho_file.undefined_symbols.items) |sym_index| {54 if (macho_file.getInternalObject()) |obj| {
54 const sym = macho_file.getSymbol(sym_index);55 for (obj.force_undefined.items) |sym_index| {
55 try markSymbol(sym, roots, macho_file);56 const ref = obj.getSymbolRef(sym_index, macho_file);
56 }57 if (ref.getFile(macho_file) != null) {
58 const sym = ref.getSymbol(macho_file).?;
59 try markSymbol(sym, roots, macho_file);
60 }
61 }
5762
58 for (&[_]?Symbol.Index{63 for (&[_]?Symbol.Index{
59 macho_file.entry_index,64 obj.entry_index,
60 macho_file.dyld_stub_binder_index,65 obj.dyld_stub_binder_index,
61 macho_file.objc_msg_send_index,66 obj.objc_msg_send_index,
62 }) |index| {67 }) |index| {
63 if (index) |idx| {68 if (index) |idx| {
64 const sym = macho_file.getSymbol(idx);69 const ref = obj.getSymbolRef(idx, macho_file);
65 try markSymbol(sym, roots, macho_file);70 if (ref.getFile(macho_file) != null) {
71 const sym = ref.getSymbol(macho_file).?;
72 try markSymbol(sym, roots, macho_file);
73 }
74 }
66 }75 }
67 }76 }
68}77}
...@@ -88,8 +97,9 @@ fn mark(roots: []*Atom, objects: []const File.Index, macho_file: *MachO) void {...@@ -88,8 +97,9 @@ fn mark(roots: []*Atom, objects: []const File.Index, macho_file: *MachO) void {
88 loop = false;97 loop = false;
8998
90 for (objects) |index| {99 for (objects) |index| {
91 for (macho_file.getFile(index).?.getAtoms()) |atom_index| {100 const file = macho_file.getFile(index).?;
92 const atom = macho_file.getAtom(atom_index).?;101 for (file.getAtoms()) |atom_index| {
102 const atom = file.getAtom(atom_index) orelse continue;
93 const isec = atom.getInputSection(macho_file);103 const isec = atom.getInputSection(macho_file);
94 if (isec.isDontDeadStripIfReferencesLive() and104 if (isec.isDontDeadStripIfReferencesLive() and
95 !(mem.eql(u8, isec.sectName(), "__eh_frame") or105 !(mem.eql(u8, isec.sectName(), "__eh_frame") or
...@@ -119,16 +129,20 @@ fn markLive(atom: *Atom, macho_file: *MachO) void {...@@ -119,16 +129,20 @@ fn markLive(atom: *Atom, macho_file: *MachO) void {
119129
120 for (atom.getRelocs(macho_file)) |rel| {130 for (atom.getRelocs(macho_file)) |rel| {
121 const target_atom = switch (rel.tag) {131 const target_atom = switch (rel.tag) {
122 .local => rel.getTargetAtom(macho_file),132 .local => rel.getTargetAtom(atom.*, macho_file),
123 .@"extern" => rel.getTargetSymbol(macho_file).getAtom(macho_file),133 .@"extern" => blk: {
134 const ref = rel.getTargetSymbolRef(atom.*, macho_file);
135 break :blk if (ref.getSymbol(macho_file)) |sym| sym.getAtom(macho_file) else null;
136 },
124 };137 };
125 if (target_atom) |ta| {138 if (target_atom) |ta| {
126 if (markAtom(ta)) markLive(ta, macho_file);139 if (markAtom(ta)) markLive(ta, macho_file);
127 }140 }
128 }141 }
129142
143 const file = atom.getFile(macho_file);
130 for (atom.getUnwindRecords(macho_file)) |cu_index| {144 for (atom.getUnwindRecords(macho_file)) |cu_index| {
131 const cu = macho_file.getUnwindRecord(cu_index);145 const cu = file.object.getUnwindRecord(cu_index);
132 const cu_atom = cu.getAtom(macho_file);146 const cu_atom = cu.getAtom(macho_file);
133 if (markAtom(cu_atom)) markLive(cu_atom, macho_file);147 if (markAtom(cu_atom)) markLive(cu_atom, macho_file);
134148
...@@ -149,8 +163,11 @@ fn markLive(atom: *Atom, macho_file: *MachO) void {...@@ -149,8 +163,11 @@ fn markLive(atom: *Atom, macho_file: *MachO) void {
149fn refersLive(atom: *Atom, macho_file: *MachO) bool {163fn refersLive(atom: *Atom, macho_file: *MachO) bool {
150 for (atom.getRelocs(macho_file)) |rel| {164 for (atom.getRelocs(macho_file)) |rel| {
151 const target_atom = switch (rel.tag) {165 const target_atom = switch (rel.tag) {
152 .local => rel.getTargetAtom(macho_file),166 .local => rel.getTargetAtom(atom.*, macho_file),
153 .@"extern" => rel.getTargetSymbol(macho_file).getAtom(macho_file),167 .@"extern" => blk: {
168 const ref = rel.getTargetSymbolRef(atom.*, macho_file);
169 break :blk if (ref.getSymbol(macho_file)) |sym| sym.getAtom(macho_file) else null;
170 },
154 };171 };
155 if (target_atom) |ta| {172 if (target_atom) |ta| {
156 if (ta.flags.alive) return true;173 if (ta.flags.alive) return true;
...@@ -161,8 +178,9 @@ fn refersLive(atom: *Atom, macho_file: *MachO) bool {...@@ -161,8 +178,9 @@ fn refersLive(atom: *Atom, macho_file: *MachO) bool {
161178
162fn prune(objects: []const File.Index, macho_file: *MachO) void {179fn prune(objects: []const File.Index, macho_file: *MachO) void {
163 for (objects) |index| {180 for (objects) |index| {
164 for (macho_file.getFile(index).?.getAtoms()) |atom_index| {181 const file = macho_file.getFile(index).?;
165 const atom = macho_file.getAtom(atom_index).?;182 for (file.getAtoms()) |atom_index| {
183 const atom = file.getAtom(atom_index) orelse continue;
166 if (atom.flags.alive and !atom.flags.visited) {184 if (atom.flags.alive and !atom.flags.visited) {
167 atom.flags.alive = false;185 atom.flags.alive = false;
168 atom.markUnwindRecordsDead(macho_file);186 atom.markUnwindRecordsDead(macho_file);
src/link/MachO/dyld_info/Rebase.zig+113-15
...@@ -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(.link_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
...@@ -30,11 +19,107 @@ pub fn deinit(rebase: *Rebase, gpa: Allocator) void {...@@ -30,11 +19,107 @@ pub fn deinit(rebase: *Rebase, gpa: Allocator) void {
30 rebase.buffer.deinit(gpa);19 rebase.buffer.deinit(gpa);
31}20}
3221
33pub fn size(rebase: Rebase) u64 {22pub fn updateSize(rebase: *Rebase, macho_file: *MachO) !void {
34 return @as(u64, @intCast(rebase.buffer.items.len));23 const tracy = trace(@src());
24 defer tracy.end();
25
26 const gpa = macho_file.base.comp.gpa;
27
28 var objects = try std.ArrayList(File.Index).initCapacity(gpa, macho_file.objects.items.len + 2);
29 defer objects.deinit();
30 objects.appendSliceAssumeCapacity(macho_file.objects.items);
31 if (macho_file.getZigObject()) |obj| objects.appendAssumeCapacity(obj.index);
32 if (macho_file.getInternalObject()) |obj| objects.appendAssumeCapacity(obj.index);
33
34 for (objects.items) |index| {
35 const file = macho_file.getFile(index).?;
36 for (file.getAtoms()) |atom_index| {
37 const atom = file.getAtom(atom_index) orelse continue;
38 if (!atom.flags.alive) continue;
39 if (atom.getInputSection(macho_file).isZerofill()) continue;
40 const atom_addr = atom.getAddress(macho_file);
41 const seg_id = macho_file.sections.items(.segment_id)[atom.out_n_sect];
42 const seg = macho_file.segments.items[seg_id];
43 for (atom.getRelocs(macho_file)) |rel| {
44 if (rel.type != .unsigned or rel.meta.length != 3) continue;
45 if (rel.tag == .@"extern") {
46 const sym = rel.getTargetSymbol(atom.*, macho_file);
47 if (sym.isTlvInit(macho_file)) continue;
48 if (sym.flags.import) continue;
49 }
50 const rel_offset = rel.offset - atom.off;
51 try rebase.entries.append(gpa, .{
52 .offset = atom_addr + rel_offset - seg.vmaddr,
53 .segment_id = seg_id,
54 });
55 }
56 }
57 }
58
59 if (macho_file.zig_got_sect_index) |sid| {
60 const seg_id = macho_file.sections.items(.segment_id)[sid];
61 const seg = macho_file.segments.items[seg_id];
62 for (0..macho_file.zig_got.entries.items.len) |idx| {
63 const addr = macho_file.zig_got.entryAddress(@intCast(idx), macho_file);
64 try rebase.entries.append(gpa, .{
65 .offset = addr - seg.vmaddr,
66 .segment_id = seg_id,
67 });
68 }
69 }
70
71 if (macho_file.got_sect_index) |sid| {
72 const seg_id = macho_file.sections.items(.segment_id)[sid];
73 const seg = macho_file.segments.items[seg_id];
74 for (macho_file.got.symbols.items, 0..) |ref, idx| {
75 const sym = ref.getSymbol(macho_file).?;
76 const addr = macho_file.got.getAddress(@intCast(idx), macho_file);
77 if (!sym.flags.import) {
78 try rebase.entries.append(gpa, .{
79 .offset = addr - seg.vmaddr,
80 .segment_id = seg_id,
81 });
82 }
83 }
84 }
85
86 if (macho_file.la_symbol_ptr_sect_index) |sid| {
87 const sect = macho_file.sections.items(.header)[sid];
88 const seg_id = macho_file.sections.items(.segment_id)[sid];
89 const seg = macho_file.segments.items[seg_id];
90 for (macho_file.stubs.symbols.items, 0..) |ref, idx| {
91 const sym = ref.getSymbol(macho_file).?;
92 const addr = sect.addr + idx * @sizeOf(u64);
93 const rebase_entry = Rebase.Entry{
94 .offset = addr - seg.vmaddr,
95 .segment_id = seg_id,
96 };
97 if ((sym.flags.import and !sym.flags.weak) or !sym.flags.import) {
98 try rebase.entries.append(gpa, rebase_entry);
99 }
100 }
101 }
102
103 if (macho_file.tlv_ptr_sect_index) |sid| {
104 const seg_id = macho_file.sections.items(.segment_id)[sid];
105 const seg = macho_file.segments.items[seg_id];
106 for (macho_file.tlv_ptr.symbols.items, 0..) |ref, idx| {
107 const sym = ref.getSymbol(macho_file).?;
108 const addr = macho_file.tlv_ptr.getAddress(@intCast(idx), macho_file);
109 if (!sym.flags.import) {
110 try rebase.entries.append(gpa, .{
111 .offset = addr - seg.vmaddr,
112 .segment_id = seg_id,
113 });
114 }
115 }
116 }
117
118 try rebase.finalize(gpa);
119 macho_file.dyld_info_cmd.rebase_size = mem.alignForward(u32, @intCast(rebase.buffer.items.len), @alignOf(u64));
35}120}
36121
37pub fn finalize(rebase: *Rebase, gpa: Allocator) !void {122fn finalize(rebase: *Rebase, gpa: Allocator) !void {
38 if (rebase.entries.items.len == 0) return;123 if (rebase.entries.items.len == 0) return;
39124
40 const writer = rebase.buffer.writer(gpa);125 const writer = rebase.buffer.writer(gpa);
...@@ -198,7 +283,6 @@ fn done(writer: anytype) !void {...@@ -198,7 +283,6 @@ fn done(writer: anytype) !void {
198}283}
199284
200pub fn write(rebase: Rebase, writer: anytype) !void {285pub fn write(rebase: Rebase, writer: anytype) !void {
201 if (rebase.size() == 0) return;
202 try writer.writeAll(rebase.buffer.items);286 try writer.writeAll(rebase.buffer.items);
203}287}
204288
...@@ -574,3 +658,17 @@ test "rebase - composite" {...@@ -574,3 +658,17 @@ test "rebase - composite" {
574 macho.REBASE_OPCODE_DONE,658 macho.REBASE_OPCODE_DONE,
575 }, rebase.buffer.items);659 }, rebase.buffer.items);
576}660}
661
662const std = @import("std");
663const assert = std.debug.assert;
664const leb = std.leb;
665const log = std.log.scoped(.link_dyld_info);
666const macho = std.macho;
667const mem = std.mem;
668const testing = std.testing;
669const trace = @import("../../../tracy.zig").trace;
670
671const Allocator = mem.Allocator;
672const File = @import("../file.zig").File;
673const MachO = @import("../../MachO.zig");
674const Rebase = @This();
src/link/MachO/dyld_info/Trie.zig+262-453
...@@ -28,463 +28,310 @@...@@ -28,463 +28,310 @@
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(.macho);
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 const 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;
11831
119 try self.edges.append(allocator, .{32/// The root node of the trie.
120 .from = self,33root: ?Node.Index = null,
121 .to = node,34buffer: std.ArrayListUnmanaged(u8) = .{},
122 .label = try allocator.dupe(u8, label),35nodes: std.MultiArrayList(Node) = .{},
123 });36edges: std.ArrayListUnmanaged(Edge) = .{},
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 }
18837
189 return nread;38/// Insert a symbol into the trie, updating the prefixes in the process.
190 }39/// This operation may change the layout of the trie by splicing edges in
40/// certain circumstances.
41fn put(self: *Trie, allocator: Allocator, symbol: ExportSymbol) !void {
42 // const tracy = trace(@src());
43 // defer tracy.end();
44
45 const node_index = try self.putNode(self.root.?, allocator, symbol.name);
46 const slice = self.nodes.slice();
47 slice.items(.is_terminal)[node_index] = true;
48 slice.items(.vmaddr_offset)[node_index] = symbol.vmaddr_offset;
49 slice.items(.export_flags)[node_index] = symbol.export_flags;
50}
19151
192 /// Writes this node to a byte stream.52/// Inserts a new node starting at `node_index`.
193 /// The children of this node *are* not written to the byte stream53fn putNode(self: *Trie, node_index: Node.Index, allocator: Allocator, label: []const u8) !Node.Index {
194 /// recursively. To write all nodes to a byte stream in sequence,54 // Check for match with edges from this node.
195 /// iterate over `Trie.ordered_nodes` and call this method on each node.55 for (self.nodes.items(.edges)[node_index].items) |edge_index| {
196 /// This is one of the requirements of the MachO.56 const edge = &self.edges.items[edge_index];
197 /// Panics if `finalize` was not called before calling this method.57 const match = mem.indexOfDiff(u8, edge.label, label) orelse return edge.node;
198 fn write(self: Node, writer: anytype) !void {58 if (match == 0) continue;
199 assert(!self.node_dirty);59 if (match == edge.label.len) return self.putNode(edge.node, allocator, label[match..]);
200 if (self.terminal_info) |info| {60
201 // Terminal node info: encode export flags and vmaddr offset of this symbol.61 // Found a match, need to splice up nodes.
202 var info_buf: [@sizeOf(u64) * 2]u8 = undefined;62 // From: A -> B
203 var info_stream = std.io.fixedBufferStream(&info_buf);63 // To: A -> C -> B
204 // TODO Implement for special flags.64 const mid_index = try self.addNode(allocator);
205 assert(info.export_flags & macho.EXPORT_SYMBOL_FLAGS_REEXPORT == 0 and65 const to_label = edge.label[match..];
206 info.export_flags & macho.EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER == 0);66 const to_node = edge.node;
207 try leb.writeUleb128(info_stream.writer(), info.export_flags);67 edge.node = mid_index;
208 try leb.writeUleb128(info_stream.writer(), info.vmaddr_offset);68 edge.label = label[0..match];
20969
210 // Encode the size of the terminal node info.70 const new_edge_index = try self.addEdge(allocator);
211 var size_buf: [@sizeOf(u64)]u8 = undefined;71 const new_edge = &self.edges.items[new_edge_index];
212 var size_stream = std.io.fixedBufferStream(&size_buf);72 new_edge.node = to_node;
213 try leb.writeUleb128(size_stream.writer(), info_stream.pos);73 new_edge.label = to_label;
21474 try self.nodes.items(.edges)[mid_index].append(allocator, new_edge_index);
215 // Now, write them to the output stream.75
216 try writer.writeAll(size_buf[0..size_stream.pos]);76 return if (match == label.len) mid_index else self.putNode(mid_index, allocator, label[match..]);
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 }77 }
23278
233 const FinalizeResult = struct {79 // Add a new node.
234 /// Current size of this node in bytes.80 const new_node_index = try self.addNode(allocator);
235 node_size: u64,81 const new_edge_index = try self.addEdge(allocator);
23682 const new_edge = &self.edges.items[new_edge_index];
237 /// True if the trie offset of this node in the output byte stream83 new_edge.node = new_node_index;
238 /// would need updating; false otherwise.84 new_edge.label = label;
239 updated: bool,85 try self.nodes.items(.edges)[node_index].append(allocator, new_edge_index);
240 };
24186
242 /// Updates offset of this node in the output byte stream.87 return new_node_index;
243 fn finalize(self: *Node, offset_in_trie: u64) !FinalizeResult {88}
244 var stream = std.io.countingWriter(std.io.null_writer);
245 const 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
25689
257 for (self.edges.items) |edge| {90pub fn updateSize(self: *Trie, macho_file: *MachO) !void {
258 const next_node_offset = edge.to.trie_offset orelse 0;91 const tracy = trace(@src());
259 node_size += edge.label.len + 1;92 defer tracy.end();
260 try leb.writeUleb128(writer, next_node_offset);93
94 const gpa = macho_file.base.comp.gpa;
95
96 try self.init(gpa);
97 try self.nodes.ensureUnusedCapacity(gpa, macho_file.resolver.values.items.len * 2);
98 try self.edges.ensureUnusedCapacity(gpa, macho_file.resolver.values.items.len * 2);
99
100 const seg = macho_file.getTextSegment();
101 for (macho_file.resolver.values.items) |ref| {
102 if (ref.getFile(macho_file) == null) continue;
103 const sym = ref.getSymbol(macho_file).?;
104 if (!sym.flags.@"export") continue;
105 if (sym.getAtom(macho_file)) |atom| if (!atom.flags.alive) continue;
106 var flags: u64 = if (sym.flags.abs)
107 macho.EXPORT_SYMBOL_FLAGS_KIND_ABSOLUTE
108 else if (sym.flags.tlv)
109 macho.EXPORT_SYMBOL_FLAGS_KIND_THREAD_LOCAL
110 else
111 macho.EXPORT_SYMBOL_FLAGS_KIND_REGULAR;
112 if (sym.flags.weak) {
113 flags |= macho.EXPORT_SYMBOL_FLAGS_WEAK_DEFINITION;
114 macho_file.weak_defines = true;
115 macho_file.binds_to_weak = true;
261 }116 }
262117 try self.put(gpa, .{
263 const trie_offset = self.trie_offset orelse 0;118 .name = sym.getName(macho_file),
264 const updated = offset_in_trie != trie_offset;119 .vmaddr_offset = sym.getAddress(.{ .stubs = false }, macho_file) - seg.vmaddr,
265 self.trie_offset = offset_in_trie;120 .export_flags = flags,
266 self.node_dirty = false;121 });
267 node_size += stream.bytes_written;
268
269 return FinalizeResult{ .node_size = node_size, .updated = updated };
270 }122 }
271};
272
273/// The root node of the trie.
274root: ?*Node = null,
275123
276/// If you want to access nodes ordered in DFS fashion,124 try self.finalize(gpa);
277/// you should call `finalize` first since the nodes
278/// in this container are not guaranteed to not be stale
279/// if more insertions took place after the last `finalize`
280/// call.
281ordered_nodes: std.ArrayListUnmanaged(*Node) = .{},
282125
283/// The size of the trie in bytes.126 macho_file.dyld_info_cmd.export_size = mem.alignForward(u32, @intCast(self.buffer.items.len), @alignOf(u64));
284/// This value may be outdated if there were additional
285/// insertions performed after `finalize` was called.
286/// Call `finalize` before accessing this value to ensure
287/// it is up-to-date.
288size: u64 = 0,
289
290/// Number of nodes currently in the trie.
291node_count: usize = 0,
292
293trie_dirty: bool = true,
294
295/// Export symbol that is to be placed in the trie.
296pub const ExportSymbol = struct {
297 /// Name of the symbol.
298 name: []const u8,
299
300 /// Offset of this symbol's virtual memory address from the beginning
301 /// of the __TEXT segment.
302 vmaddr_offset: u64,
303
304 /// Export flags of this exported symbol.
305 export_flags: u64,
306};
307
308/// Insert a symbol into the trie, updating the prefixes in the process.
309/// This operation may change the layout of the trie by splicing edges in
310/// certain circumstances.
311pub fn put(self: *Trie, allocator: Allocator, symbol: ExportSymbol) !void {
312 const node = try self.root.?.put(allocator, symbol.name);
313 node.terminal_info = .{
314 .vmaddr_offset = symbol.vmaddr_offset,
315 .export_flags = symbol.export_flags,
316 };
317 self.trie_dirty = true;
318}127}
319128
320/// Finalizes this trie for writing to a byte stream.129/// Finalizes this trie for writing to a byte stream.
321/// This step performs multiple passes through the trie ensuring130/// This step performs multiple passes through the trie ensuring
322/// there are no gaps after every `Node` is ULEB128 encoded.131/// there are no gaps after every `Node` is ULEB128 encoded.
323/// Call this method before trying to `write` the trie to a byte stream.132/// Call this method before trying to `write` the trie to a byte stream.
324pub fn finalize(self: *Trie, allocator: Allocator) !void {133fn finalize(self: *Trie, allocator: Allocator) !void {
325 if (!self.trie_dirty) return;134 const tracy = trace(@src());
135 defer tracy.end();
326136
327 self.ordered_nodes.shrinkRetainingCapacity(0);137 var ordered_nodes = std.ArrayList(Node.Index).init(allocator);
328 try self.ordered_nodes.ensureTotalCapacity(allocator, self.node_count);138 defer ordered_nodes.deinit();
139 try ordered_nodes.ensureTotalCapacityPrecise(self.nodes.items(.is_terminal).len);
329140
330 var fifo = std.fifo.LinearFifo(*Node, .Dynamic).init(allocator);141 var fifo = std.fifo.LinearFifo(Node.Index, .Dynamic).init(allocator);
331 defer fifo.deinit();142 defer fifo.deinit();
332143
333 try fifo.writeItem(self.root.?);144 try fifo.writeItem(self.root.?);
334145
335 while (fifo.readItem()) |next| {146 while (fifo.readItem()) |next_index| {
336 for (next.edges.items) |*edge| {147 const edges = &self.nodes.items(.edges)[next_index];
337 try fifo.writeItem(edge.to);148 for (edges.items) |edge_index| {
149 const edge = self.edges.items[edge_index];
150 try fifo.writeItem(edge.node);
338 }151 }
339 self.ordered_nodes.appendAssumeCapacity(next);152 ordered_nodes.appendAssumeCapacity(next_index);
340 }153 }
341154
342 var more: bool = true;155 var more: bool = true;
156 var size: u32 = 0;
343 while (more) {157 while (more) {
344 self.size = 0;158 size = 0;
345 more = false;159 more = false;
346 for (self.ordered_nodes.items) |node| {160 for (ordered_nodes.items) |node_index| {
347 const res = try node.finalize(self.size);161 const res = try self.finalizeNode(node_index, size);
348 self.size += res.node_size;162 size += res.node_size;
349 if (res.updated) more = true;163 if (res.updated) more = true;
350 }164 }
351 }165 }
352166
353 self.trie_dirty = false;167 try self.buffer.ensureTotalCapacityPrecise(allocator, size);
168 for (ordered_nodes.items) |node_index| {
169 try self.writeNode(node_index, self.buffer.writer(allocator));
170 }
354}171}
355172
356const ReadError = error{173const FinalizeNodeResult = struct {
357 OutOfMemory,174 /// Current size of this node in bytes.
358 EndOfStream,175 node_size: u32,
359 Overflow,176
177 /// True if the trie offset of this node in the output byte stream
178 /// would need updating; false otherwise.
179 updated: bool,
360};180};
361181
362/// Parse the trie from a byte stream.182/// Updates offset of this node in the output byte stream.
363pub fn read(self: *Trie, allocator: Allocator, reader: anytype) ReadError!usize {183fn finalizeNode(self: *Trie, node_index: Node.Index, offset_in_trie: u32) !FinalizeNodeResult {
364 return self.root.?.read(allocator, reader);184 var stream = std.io.countingWriter(std.io.null_writer);
365}185 const writer = stream.writer();
186 const slice = self.nodes.slice();
187
188 var node_size: u32 = 0;
189 if (slice.items(.is_terminal)[node_index]) {
190 const export_flags = slice.items(.export_flags)[node_index];
191 const vmaddr_offset = slice.items(.vmaddr_offset)[node_index];
192 try leb.writeULEB128(writer, export_flags);
193 try leb.writeULEB128(writer, vmaddr_offset);
194 try leb.writeULEB128(writer, stream.bytes_written);
195 } else {
196 node_size += 1; // 0x0 for non-terminal nodes
197 }
198 node_size += 1; // 1 byte for edge count
366199
367/// Write the trie to a byte stream.200 for (slice.items(.edges)[node_index].items) |edge_index| {
368/// Panics if the trie was not finalized using `finalize` before calling this method.201 const edge = &self.edges.items[edge_index];
369pub fn write(self: Trie, writer: anytype) !void {202 const next_node_offset = slice.items(.trie_offset)[edge.node];
370 assert(!self.trie_dirty);203 node_size += @intCast(edge.label.len + 1);
371 for (self.ordered_nodes.items) |node| {204 try leb.writeULEB128(writer, next_node_offset);
372 try node.write(writer);
373 }205 }
206
207 const trie_offset = slice.items(.trie_offset)[node_index];
208 const updated = offset_in_trie != trie_offset;
209 slice.items(.trie_offset)[node_index] = offset_in_trie;
210 node_size += @intCast(stream.bytes_written);
211
212 return .{ .node_size = node_size, .updated = updated };
374}213}
375214
376pub fn init(self: *Trie, allocator: Allocator) !void {215fn init(self: *Trie, allocator: Allocator) !void {
377 assert(self.root == null);216 assert(self.root == null);
378 const root = try allocator.create(Node);217 self.root = try self.addNode(allocator);
379 root.* = .{ .base = self };
380 self.root = root;
381 self.node_count += 1;
382}218}
383219
384pub fn deinit(self: *Trie, allocator: Allocator) void {220pub fn deinit(self: *Trie, allocator: Allocator) void {
385 if (self.root) |root| {221 for (self.nodes.items(.edges)) |*edges| {
386 root.deinit(allocator);222 edges.deinit(allocator);
387 allocator.destroy(root);
388 }223 }
389 self.ordered_nodes.deinit(allocator);224 self.nodes.deinit(allocator);
225 self.edges.deinit(allocator);
226 self.buffer.deinit(allocator);
390}227}
391228
392test "Trie node count" {229pub fn write(self: Trie, writer: anytype) !void {
393 const gpa = testing.allocator;230 if (self.buffer.items.len == 0) return;
394 var trie: Trie = .{};231 try writer.writeAll(self.buffer.items);
395 defer trie.deinit(gpa);232}
396 try trie.init(gpa);
397233
398 try testing.expectEqual(@as(usize, 1), trie.node_count);234/// Writes this node to a byte stream.
399 try testing.expect(trie.root != null);235/// The children of this node *are* not written to the byte stream
236/// recursively. To write all nodes to a byte stream in sequence,
237/// iterate over `Trie.ordered_nodes` and call this method on each node.
238/// This is one of the requirements of the MachO.
239/// Panics if `finalize` was not called before calling this method.
240fn writeNode(self: *Trie, node_index: Node.Index, writer: anytype) !void {
241 const slice = self.nodes.slice();
242 const edges = slice.items(.edges)[node_index];
243 const is_terminal = slice.items(.is_terminal)[node_index];
244 const export_flags = slice.items(.export_flags)[node_index];
245 const vmaddr_offset = slice.items(.vmaddr_offset)[node_index];
246
247 if (is_terminal) {
248 // Terminal node info: encode export flags and vmaddr offset of this symbol.
249 var info_buf: [@sizeOf(u64) * 2]u8 = undefined;
250 var info_stream = std.io.fixedBufferStream(&info_buf);
251 // TODO Implement for special flags.
252 assert(export_flags & macho.EXPORT_SYMBOL_FLAGS_REEXPORT == 0 and
253 export_flags & macho.EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER == 0);
254 try leb.writeULEB128(info_stream.writer(), export_flags);
255 try leb.writeULEB128(info_stream.writer(), vmaddr_offset);
256
257 // Encode the size of the terminal node info.
258 var size_buf: [@sizeOf(u64)]u8 = undefined;
259 var size_stream = std.io.fixedBufferStream(&size_buf);
260 try leb.writeULEB128(size_stream.writer(), info_stream.pos);
261
262 // Now, write them to the output stream.
263 try writer.writeAll(size_buf[0..size_stream.pos]);
264 try writer.writeAll(info_buf[0..info_stream.pos]);
265 } else {
266 // Non-terminal node is delimited by 0 byte.
267 try writer.writeByte(0);
268 }
269 // Write number of edges (max legal number of edges is 256).
270 try writer.writeByte(@as(u8, @intCast(edges.items.len)));
271
272 for (edges.items) |edge_index| {
273 const edge = self.edges.items[edge_index];
274 // Write edge label and offset to next node in trie.
275 try writer.writeAll(edge.label);
276 try writer.writeByte(0);
277 try leb.writeULEB128(writer, slice.items(.trie_offset)[edge.node]);
278 }
279}
400280
401 try trie.put(gpa, .{281fn addNode(self: *Trie, allocator: Allocator) !Node.Index {
402 .name = "_main",282 const index: Node.Index = @intCast(try self.nodes.addOne(allocator));
403 .vmaddr_offset = 0,283 self.nodes.set(index, .{});
404 .export_flags = 0,284 return index;
405 });285}
406 try testing.expectEqual(@as(usize, 2), trie.node_count);
407286
408 // Inserting the same node shouldn't update the trie.287fn addEdge(self: *Trie, allocator: Allocator) !Edge.Index {
409 try trie.put(gpa, .{288 const index: Edge.Index = @intCast(self.edges.items.len);
410 .name = "_main",289 const edge = try self.edges.addOne(allocator);
411 .vmaddr_offset = 0,290 edge.* = .{};
412 .export_flags = 0,291 return index;
413 });292}
414 try testing.expectEqual(@as(usize, 2), trie.node_count);
415293
416 try trie.put(gpa, .{294/// Export symbol that is to be placed in the trie.
417 .name = "__mh_execute_header",295pub const ExportSymbol = struct {
418 .vmaddr_offset = 0x1000,296 /// Name of the symbol.
419 .export_flags = 0,297 name: []const u8,
420 });
421 try testing.expectEqual(@as(usize, 4), trie.node_count);
422298
423 // Inserting the same node shouldn't update the trie.299 /// Offset of this symbol's virtual memory address from the beginning
424 try trie.put(gpa, .{300 /// of the __TEXT segment.
425 .name = "__mh_execute_header",301 vmaddr_offset: u64,
426 .vmaddr_offset = 0x1000,
427 .export_flags = 0,
428 });
429 try testing.expectEqual(@as(usize, 4), trie.node_count);
430 try trie.put(gpa, .{
431 .name = "_main",
432 .vmaddr_offset = 0,
433 .export_flags = 0,
434 });
435 try testing.expectEqual(@as(usize, 4), trie.node_count);
436}
437302
438test "Trie basic" {303 /// Export flags of this exported symbol.
439 const gpa = testing.allocator;304 export_flags: u64,
440 var trie: Trie = .{};305};
441 defer trie.deinit(gpa);
442 try trie.init(gpa);
443306
444 // root --- _st ---> node307const Node = struct {
445 try trie.put(gpa, .{308 is_terminal: bool = false,
446 .name = "_st",
447 .vmaddr_offset = 0,
448 .export_flags = 0,
449 });
450 try testing.expect(trie.root.?.edges.items.len == 1);
451 try testing.expect(mem.eql(u8, trie.root.?.edges.items[0].label, "_st"));
452
453 {
454 // root --- _st ---> node --- art ---> node
455 try trie.put(gpa, .{
456 .name = "_start",
457 .vmaddr_offset = 0,
458 .export_flags = 0,
459 });
460 try testing.expect(trie.root.?.edges.items.len == 1);
461309
462 const nextEdge = &trie.root.?.edges.items[0];310 /// Export flags associated with this exported symbol.
463 try testing.expect(mem.eql(u8, nextEdge.label, "_st"));311 export_flags: u64 = 0,
464 try testing.expect(nextEdge.to.edges.items.len == 1);
465 try testing.expect(mem.eql(u8, nextEdge.to.edges.items[0].label, "art"));
466 }
467 {
468 // root --- _ ---> node --- st ---> node --- art ---> node
469 // |
470 // | --- main ---> node
471 try trie.put(gpa, .{
472 .name = "_main",
473 .vmaddr_offset = 0,
474 .export_flags = 0,
475 });
476 try testing.expect(trie.root.?.edges.items.len == 1);
477312
478 const nextEdge = &trie.root.?.edges.items[0];313 /// VM address offset wrt to the section this symbol is defined against.
479 try testing.expect(mem.eql(u8, nextEdge.label, "_"));314 vmaddr_offset: u64 = 0,
480 try testing.expect(nextEdge.to.edges.items.len == 2);
481 try testing.expect(mem.eql(u8, nextEdge.to.edges.items[0].label, "st"));
482 try testing.expect(mem.eql(u8, nextEdge.to.edges.items[1].label, "main"));
483315
484 const nextNextEdge = &nextEdge.to.edges.items[0];316 /// Offset of this node in the trie output byte stream.
485 try testing.expect(mem.eql(u8, nextNextEdge.to.edges.items[0].label, "art"));317 trie_offset: u32 = 0,
486 }318
487}319 /// List of all edges originating from this node.
320 edges: std.ArrayListUnmanaged(Edge.Index) = .{},
321
322 const Index = u32;
323};
324
325/// Edge connecting nodes in the trie.
326const Edge = struct {
327 /// Target node in the trie.
328 node: Node.Index = 0,
329
330 /// Matching prefix.
331 label: []const u8 = "",
332
333 const Index = u32;
334};
488335
489fn expectEqualHexStrings(expected: []const u8, given: []const u8) !void {336fn expectEqualHexStrings(expected: []const u8, given: []const u8) !void {
490 assert(expected.len > 0);337 assert(expected.len > 0);
...@@ -502,7 +349,7 @@ fn expectEqualHexStrings(expected: []const u8, given: []const u8) !void {...@@ -502,7 +349,7 @@ fn expectEqualHexStrings(expected: []const u8, given: []const u8) !void {
502}349}
503350
504test "write Trie to a byte stream" {351test "write Trie to a byte stream" {
505 var gpa = testing.allocator;352 const gpa = testing.allocator;
506 var trie: Trie = .{};353 var trie: Trie = .{};
507 defer trie.deinit(gpa);354 defer trie.deinit(gpa);
508 try trie.init(gpa);355 try trie.init(gpa);
...@@ -519,7 +366,6 @@ test "write Trie to a byte stream" {...@@ -519,7 +366,6 @@ test "write Trie to a byte stream" {
519 });366 });
520367
521 try trie.finalize(gpa);368 try trie.finalize(gpa);
522 try trie.finalize(gpa); // Finalizing mulitple times is a nop subsequently unless we add new nodes.
523369
524 const exp_buffer = [_]u8{370 const exp_buffer = [_]u8{
525 0x0, 0x1, // node root371 0x0, 0x1, // node root
...@@ -531,51 +377,7 @@ test "write Trie to a byte stream" {...@@ -531,51 +377,7 @@ test "write Trie to a byte stream" {
531 0x2, 0x0, 0x0, 0x0, // terminal node377 0x2, 0x0, 0x0, 0x0, // terminal node
532 0x3, 0x0, 0x80, 0x20, 0x0, // terminal node378 0x3, 0x0, 0x80, 0x20, 0x0, // terminal node
533 };379 };
534380 try expectEqualHexStrings(&exp_buffer, trie.buffer.items);
535 const buffer = try gpa.alloc(u8, trie.size);
536 defer gpa.free(buffer);
537 var stream = std.io.fixedBufferStream(buffer);
538 {
539 _ = try trie.write(stream.writer());
540 try expectEqualHexStrings(&exp_buffer, buffer);
541 }
542 {
543 // Writing finalized trie again should yield the same result.
544 try stream.seekTo(0);
545 _ = try trie.write(stream.writer());
546 try expectEqualHexStrings(&exp_buffer, buffer);
547 }
548}
549
550test "parse Trie from byte stream" {
551 const gpa = testing.allocator;
552
553 const in_buffer = [_]u8{
554 0x0, 0x1, // node root
555 0x5f, 0x0, 0x5, // edge '_'
556 0x0, 0x2, // non-terminal node
557 0x5f, 0x6d, 0x68, 0x5f, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, // edge '_mh_execute_header'
558 0x65, 0x5f, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x0, 0x21, // edge '_mh_execute_header'
559 0x6d, 0x61, 0x69, 0x6e, 0x0, 0x25, // edge 'main'
560 0x2, 0x0, 0x0, 0x0, // terminal node
561 0x3, 0x0, 0x80, 0x20, 0x0, // terminal node
562 };
563
564 var in_stream = std.io.fixedBufferStream(&in_buffer);
565 var trie: Trie = .{};
566 defer trie.deinit(gpa);
567 try trie.init(gpa);
568 const nread = try trie.read(gpa, in_stream.reader());
569
570 try testing.expect(nread == in_buffer.len);
571
572 try trie.finalize(gpa);
573
574 const out_buffer = try gpa.alloc(u8, trie.size);
575 defer gpa.free(out_buffer);
576 var out_stream = std.io.fixedBufferStream(out_buffer);
577 _ = try trie.write(out_stream.writer());
578 try expectEqualHexStrings(&in_buffer, out_buffer);
579}381}
580382
581test "ordering bug" {383test "ordering bug" {
...@@ -602,11 +404,18 @@ test "ordering bug" {...@@ -602,11 +404,18 @@ test "ordering bug" {
602 0x88, 0x80, 0x02, 0x01, 0x73, 0x53, 0x74, 0x72,404 0x88, 0x80, 0x02, 0x01, 0x73, 0x53, 0x74, 0x72,
603 0x00, 0x12, 0x03, 0x00, 0xD8, 0x0A, 0x00,405 0x00, 0x12, 0x03, 0x00, 0xD8, 0x0A, 0x00,
604 };406 };
605407 try expectEqualHexStrings(&exp_buffer, trie.buffer.items);
606 const buffer = try gpa.alloc(u8, trie.size);
607 defer gpa.free(buffer);
608 var stream = std.io.fixedBufferStream(buffer);
609 // Writing finalized trie again should yield the same result.
610 _ = try trie.write(stream.writer());
611 try expectEqualHexStrings(&exp_buffer, buffer);
612}408}
409
410const assert = std.debug.assert;
411const leb = std.leb;
412const log = std.log.scoped(.macho);
413const macho = std.macho;
414const mem = std.mem;
415const std = @import("std");
416const testing = std.testing;
417const trace = @import("../../../tracy.zig").trace;
418
419const Allocator = mem.Allocator;
420const MachO = @import("../../MachO.zig");
421const Trie = @This();
src/link/MachO/dyld_info/bind.zig+259-34
...@@ -1,28 +1,19 @@...@@ -1,28 +1,19 @@
1const std = @import("std");
2const assert = std.debug.assert;
3const leb = std.leb;
4const log = std.log.scoped(.link_dyld_info);
5const macho = std.macho;
6const testing = std.testing;
7
8const Allocator = std.mem.Allocator;
9const MachO = @import("../../MachO.zig");
10const Symbol = @import("../Symbol.zig");
11
12pub const Entry = struct {1pub const Entry = struct {
13 target: Symbol.Index,2 target: MachO.Ref,
14 offset: u64,3 offset: u64,
15 segment_id: u8,4 segment_id: u8,
16 addend: i64,5 addend: i64,
176
18 pub fn lessThan(ctx: *MachO, entry: Entry, other: Entry) bool {7 pub fn lessThan(ctx: *MachO, entry: Entry, other: Entry) bool {
8 _ = ctx;
19 if (entry.segment_id == other.segment_id) {9 if (entry.segment_id == other.segment_id) {
20 if (entry.target == other.target) {10 if (entry.target.eql(other.target)) {
21 return entry.offset < other.offset;11 return entry.offset < other.offset;
22 }12 }
23 const entry_name = ctx.getSymbol(entry.target).getName(ctx);13 if (entry.target.file == other.target.file) {
24 const other_name = ctx.getSymbol(other.target).getName(ctx);14 return entry.target.index < other.target.index;
25 return std.mem.lessThan(u8, entry_name, other_name);15 }
16 return entry.target.file < other.target.file;
26 }17 }
27 return entry.segment_id < other.segment_id;18 return entry.segment_id < other.segment_id;
28 }19 }
...@@ -39,11 +30,109 @@ pub const Bind = struct {...@@ -39,11 +30,109 @@ pub const Bind = struct {
39 self.buffer.deinit(gpa);30 self.buffer.deinit(gpa);
40 }31 }
4132
42 pub fn size(self: Self) u64 {33 pub fn updateSize(self: *Self, macho_file: *MachO) !void {
43 return @intCast(self.buffer.items.len);34 const tracy = trace(@src());
35 defer tracy.end();
36
37 const gpa = macho_file.base.comp.gpa;
38 const cpu_arch = macho_file.getTarget().cpu.arch;
39
40 var objects = try std.ArrayList(File.Index).initCapacity(gpa, macho_file.objects.items.len + 2);
41 defer objects.deinit();
42 objects.appendSliceAssumeCapacity(macho_file.objects.items);
43 if (macho_file.getZigObject()) |obj| objects.appendAssumeCapacity(obj.index);
44 if (macho_file.getInternalObject()) |obj| objects.appendAssumeCapacity(obj.index);
45
46 for (objects.items) |index| {
47 const file = macho_file.getFile(index).?;
48 for (file.getAtoms()) |atom_index| {
49 const atom = file.getAtom(atom_index) orelse continue;
50 if (!atom.flags.alive) continue;
51 if (atom.getInputSection(macho_file).isZerofill()) continue;
52 const atom_addr = atom.getAddress(macho_file);
53 const relocs = atom.getRelocs(macho_file);
54 const seg_id = macho_file.sections.items(.segment_id)[atom.out_n_sect];
55 const seg = macho_file.segments.items[seg_id];
56 for (relocs) |rel| {
57 if (rel.type != .unsigned or rel.meta.length != 3 or rel.tag != .@"extern") continue;
58 const rel_offset = rel.offset - atom.off;
59 const addend = rel.addend + rel.getRelocAddend(cpu_arch);
60 const sym = rel.getTargetSymbol(atom.*, macho_file);
61 if (sym.isTlvInit(macho_file)) continue;
62 const entry = Entry{
63 .target = rel.getTargetSymbolRef(atom.*, macho_file),
64 .offset = atom_addr + rel_offset - seg.vmaddr,
65 .segment_id = seg_id,
66 .addend = addend,
67 };
68 if (sym.flags.import or (!(sym.flags.@"export" and sym.flags.weak) and sym.flags.interposable)) {
69 try self.entries.append(gpa, entry);
70 }
71 }
72 }
73 }
74
75 if (macho_file.got_sect_index) |sid| {
76 const seg_id = macho_file.sections.items(.segment_id)[sid];
77 const seg = macho_file.segments.items[seg_id];
78 for (macho_file.got.symbols.items, 0..) |ref, idx| {
79 const sym = ref.getSymbol(macho_file).?;
80 const addr = macho_file.got.getAddress(@intCast(idx), macho_file);
81 const entry = Entry{
82 .target = ref,
83 .offset = addr - seg.vmaddr,
84 .segment_id = seg_id,
85 .addend = 0,
86 };
87 if (sym.flags.import or (sym.flags.@"export" and sym.flags.interposable and !sym.flags.weak)) {
88 try self.entries.append(gpa, entry);
89 }
90 }
91 }
92
93 if (macho_file.la_symbol_ptr_sect_index) |sid| {
94 const sect = macho_file.sections.items(.header)[sid];
95 const seg_id = macho_file.sections.items(.segment_id)[sid];
96 const seg = macho_file.segments.items[seg_id];
97 for (macho_file.stubs.symbols.items, 0..) |ref, idx| {
98 const sym = ref.getSymbol(macho_file).?;
99 const addr = sect.addr + idx * @sizeOf(u64);
100 const bind_entry = Entry{
101 .target = ref,
102 .offset = addr - seg.vmaddr,
103 .segment_id = seg_id,
104 .addend = 0,
105 };
106 if (sym.flags.import and sym.flags.weak) {
107 try self.entries.append(gpa, bind_entry);
108 }
109 }
110 }
111
112 if (macho_file.tlv_ptr_sect_index) |sid| {
113 const seg_id = macho_file.sections.items(.segment_id)[sid];
114 const seg = macho_file.segments.items[seg_id];
115
116 for (macho_file.tlv_ptr.symbols.items, 0..) |ref, idx| {
117 const sym = ref.getSymbol(macho_file).?;
118 const addr = macho_file.tlv_ptr.getAddress(@intCast(idx), macho_file);
119 const entry = Entry{
120 .target = ref,
121 .offset = addr - seg.vmaddr,
122 .segment_id = seg_id,
123 .addend = 0,
124 };
125 if (sym.flags.import or (sym.flags.@"export" and sym.flags.interposable and !sym.flags.weak)) {
126 try self.entries.append(gpa, entry);
127 }
128 }
129 }
130
131 try self.finalize(gpa, macho_file);
132 macho_file.dyld_info_cmd.bind_size = mem.alignForward(u32, @intCast(self.buffer.items.len), @alignOf(u64));
44 }133 }
45134
46 pub fn finalize(self: *Self, gpa: Allocator, ctx: *MachO) !void {135 fn finalize(self: *Self, gpa: Allocator, ctx: *MachO) !void {
47 if (self.entries.items.len == 0) return;136 if (self.entries.items.len == 0) return;
48137
49 const writer = self.buffer.writer(gpa);138 const writer = self.buffer.writer(gpa);
...@@ -75,7 +164,7 @@ pub const Bind = struct {...@@ -75,7 +164,7 @@ pub const Bind = struct {
75 var addend: i64 = 0;164 var addend: i64 = 0;
76 var count: usize = 0;165 var count: usize = 0;
77 var skip: u64 = 0;166 var skip: u64 = 0;
78 var target: ?Symbol.Index = null;167 var target: ?MachO.Ref = null;
79168
80 var state: enum {169 var state: enum {
81 start,170 start,
...@@ -86,7 +175,7 @@ pub const Bind = struct {...@@ -86,7 +175,7 @@ pub const Bind = struct {
86 var i: usize = 0;175 var i: usize = 0;
87 while (i < entries.len) : (i += 1) {176 while (i < entries.len) : (i += 1) {
88 const current = entries[i];177 const current = entries[i];
89 if (target == null or target.? != current.target) {178 if (target == null or !target.?.eql(current.target)) {
90 switch (state) {179 switch (state) {
91 .start => {},180 .start => {},
92 .bind_single => try doBind(writer),181 .bind_single => try doBind(writer),
...@@ -95,7 +184,7 @@ pub const Bind = struct {...@@ -95,7 +184,7 @@ pub const Bind = struct {
95 state = .start;184 state = .start;
96 target = current.target;185 target = current.target;
97186
98 const sym = ctx.getSymbol(current.target);187 const sym = current.target.getSymbol(ctx).?;
99 const name = sym.getName(ctx);188 const name = sym.getName(ctx);
100 const flags: u8 = if (sym.weakRef(ctx)) macho.BIND_SYMBOL_FLAGS_WEAK_IMPORT else 0;189 const flags: u8 = if (sym.weakRef(ctx)) macho.BIND_SYMBOL_FLAGS_WEAK_IMPORT else 0;
101 const ordinal: i16 = ord: {190 const ordinal: i16 = ord: {
...@@ -178,7 +267,6 @@ pub const Bind = struct {...@@ -178,7 +267,6 @@ pub const Bind = struct {
178 }267 }
179268
180 pub fn write(self: Self, writer: anytype) !void {269 pub fn write(self: Self, writer: anytype) !void {
181 if (self.size() == 0) return;
182 try writer.writeAll(self.buffer.items);270 try writer.writeAll(self.buffer.items);
183 }271 }
184};272};
...@@ -194,11 +282,110 @@ pub const WeakBind = struct {...@@ -194,11 +282,110 @@ pub const WeakBind = struct {
194 self.buffer.deinit(gpa);282 self.buffer.deinit(gpa);
195 }283 }
196284
197 pub fn size(self: Self) u64 {285 pub fn updateSize(self: *Self, macho_file: *MachO) !void {
198 return @intCast(self.buffer.items.len);286 const tracy = trace(@src());
287 defer tracy.end();
288
289 const gpa = macho_file.base.comp.gpa;
290 const cpu_arch = macho_file.getTarget().cpu.arch;
291
292 var objects = try std.ArrayList(File.Index).initCapacity(gpa, macho_file.objects.items.len + 2);
293 defer objects.deinit();
294 objects.appendSliceAssumeCapacity(macho_file.objects.items);
295 if (macho_file.getZigObject()) |obj| objects.appendAssumeCapacity(obj.index);
296 if (macho_file.getInternalObject()) |obj| objects.appendAssumeCapacity(obj.index);
297
298 for (objects.items) |index| {
299 const file = macho_file.getFile(index).?;
300 for (file.getAtoms()) |atom_index| {
301 const atom = file.getAtom(atom_index) orelse continue;
302 if (!atom.flags.alive) continue;
303 if (atom.getInputSection(macho_file).isZerofill()) continue;
304 const atom_addr = atom.getAddress(macho_file);
305 const relocs = atom.getRelocs(macho_file);
306 const seg_id = macho_file.sections.items(.segment_id)[atom.out_n_sect];
307 const seg = macho_file.segments.items[seg_id];
308 for (relocs) |rel| {
309 if (rel.type != .unsigned or rel.meta.length != 3 or rel.tag != .@"extern") continue;
310 const rel_offset = rel.offset - atom.off;
311 const addend = rel.addend + rel.getRelocAddend(cpu_arch);
312 const sym = rel.getTargetSymbol(atom.*, macho_file);
313 if (sym.isTlvInit(macho_file)) continue;
314 const entry = Entry{
315 .target = rel.getTargetSymbolRef(atom.*, macho_file),
316 .offset = atom_addr + rel_offset - seg.vmaddr,
317 .segment_id = seg_id,
318 .addend = addend,
319 };
320 if (!sym.isLocal() and sym.flags.weak) {
321 try self.entries.append(gpa, entry);
322 }
323 }
324 }
325 }
326
327 if (macho_file.got_sect_index) |sid| {
328 const seg_id = macho_file.sections.items(.segment_id)[sid];
329 const seg = macho_file.segments.items[seg_id];
330 for (macho_file.got.symbols.items, 0..) |ref, idx| {
331 const sym = ref.getSymbol(macho_file).?;
332 const addr = macho_file.got.getAddress(@intCast(idx), macho_file);
333 const entry = Entry{
334 .target = ref,
335 .offset = addr - seg.vmaddr,
336 .segment_id = seg_id,
337 .addend = 0,
338 };
339 if (sym.flags.weak) {
340 try self.entries.append(gpa, entry);
341 }
342 }
343 }
344
345 if (macho_file.la_symbol_ptr_sect_index) |sid| {
346 const sect = macho_file.sections.items(.header)[sid];
347 const seg_id = macho_file.sections.items(.segment_id)[sid];
348 const seg = macho_file.segments.items[seg_id];
349
350 for (macho_file.stubs.symbols.items, 0..) |ref, idx| {
351 const sym = ref.getSymbol(macho_file).?;
352 const addr = sect.addr + idx * @sizeOf(u64);
353 const bind_entry = Entry{
354 .target = ref,
355 .offset = addr - seg.vmaddr,
356 .segment_id = seg_id,
357 .addend = 0,
358 };
359 if (sym.flags.weak) {
360 try self.entries.append(gpa, bind_entry);
361 }
362 }
363 }
364
365 if (macho_file.tlv_ptr_sect_index) |sid| {
366 const seg_id = macho_file.sections.items(.segment_id)[sid];
367 const seg = macho_file.segments.items[seg_id];
368
369 for (macho_file.tlv_ptr.symbols.items, 0..) |ref, idx| {
370 const sym = ref.getSymbol(macho_file).?;
371 const addr = macho_file.tlv_ptr.getAddress(@intCast(idx), macho_file);
372 const entry = Entry{
373 .target = ref,
374 .offset = addr - seg.vmaddr,
375 .segment_id = seg_id,
376 .addend = 0,
377 };
378 if (sym.flags.weak) {
379 try self.entries.append(gpa, entry);
380 }
381 }
382 }
383
384 try self.finalize(gpa, macho_file);
385 macho_file.dyld_info_cmd.weak_bind_size = mem.alignForward(u32, @intCast(self.buffer.items.len), @alignOf(u64));
199 }386 }
200387
201 pub fn finalize(self: *Self, gpa: Allocator, ctx: *MachO) !void {388 fn finalize(self: *Self, gpa: Allocator, ctx: *MachO) !void {
202 if (self.entries.items.len == 0) return;389 if (self.entries.items.len == 0) return;
203390
204 const writer = self.buffer.writer(gpa);391 const writer = self.buffer.writer(gpa);
...@@ -230,7 +417,7 @@ pub const WeakBind = struct {...@@ -230,7 +417,7 @@ pub const WeakBind = struct {
230 var addend: i64 = 0;417 var addend: i64 = 0;
231 var count: usize = 0;418 var count: usize = 0;
232 var skip: u64 = 0;419 var skip: u64 = 0;
233 var target: ?Symbol.Index = null;420 var target: ?MachO.Ref = null;
234421
235 var state: enum {422 var state: enum {
236 start,423 start,
...@@ -241,7 +428,7 @@ pub const WeakBind = struct {...@@ -241,7 +428,7 @@ pub const WeakBind = struct {
241 var i: usize = 0;428 var i: usize = 0;
242 while (i < entries.len) : (i += 1) {429 while (i < entries.len) : (i += 1) {
243 const current = entries[i];430 const current = entries[i];
244 if (target == null or target.? != current.target) {431 if (target == null or !target.?.eql(current.target)) {
245 switch (state) {432 switch (state) {
246 .start => {},433 .start => {},
247 .bind_single => try doBind(writer),434 .bind_single => try doBind(writer),
...@@ -250,7 +437,7 @@ pub const WeakBind = struct {...@@ -250,7 +437,7 @@ pub const WeakBind = struct {
250 state = .start;437 state = .start;
251 target = current.target;438 target = current.target;
252439
253 const sym = ctx.getSymbol(current.target);440 const sym = current.target.getSymbol(ctx).?;
254 const name = sym.getName(ctx);441 const name = sym.getName(ctx);
255 const flags: u8 = 0; // TODO NON_WEAK_DEFINITION442 const flags: u8 = 0; // TODO NON_WEAK_DEFINITION
256443
...@@ -322,7 +509,6 @@ pub const WeakBind = struct {...@@ -322,7 +509,6 @@ pub const WeakBind = struct {
322 }509 }
323510
324 pub fn write(self: Self, writer: anytype) !void {511 pub fn write(self: Self, writer: anytype) !void {
325 if (self.size() == 0) return;
326 try writer.writeAll(self.buffer.items);512 try writer.writeAll(self.buffer.items);
327 }513 }
328};514};
...@@ -340,11 +526,36 @@ pub const LazyBind = struct {...@@ -340,11 +526,36 @@ pub const LazyBind = struct {
340 self.offsets.deinit(gpa);526 self.offsets.deinit(gpa);
341 }527 }
342528
343 pub fn size(self: Self) u64 {529 pub fn updateSize(self: *Self, macho_file: *MachO) !void {
344 return @intCast(self.buffer.items.len);530 const tracy = trace(@src());
531 defer tracy.end();
532
533 const gpa = macho_file.base.comp.gpa;
534
535 const sid = macho_file.la_symbol_ptr_sect_index.?;
536 const sect = macho_file.sections.items(.header)[sid];
537 const seg_id = macho_file.sections.items(.segment_id)[sid];
538 const seg = macho_file.segments.items[seg_id];
539
540 for (macho_file.stubs.symbols.items, 0..) |ref, idx| {
541 const sym = ref.getSymbol(macho_file).?;
542 const addr = sect.addr + idx * @sizeOf(u64);
543 const bind_entry = Entry{
544 .target = ref,
545 .offset = addr - seg.vmaddr,
546 .segment_id = seg_id,
547 .addend = 0,
548 };
549 if ((sym.flags.import and !sym.flags.weak) or (sym.flags.interposable and !sym.flags.weak)) {
550 try self.entries.append(gpa, bind_entry);
551 }
552 }
553
554 try self.finalize(gpa, macho_file);
555 macho_file.dyld_info_cmd.lazy_bind_size = mem.alignForward(u32, @intCast(self.buffer.items.len), @alignOf(u64));
345 }556 }
346557
347 pub fn finalize(self: *Self, gpa: Allocator, ctx: *MachO) !void {558 fn finalize(self: *Self, gpa: Allocator, ctx: *MachO) !void {
348 try self.offsets.ensureTotalCapacityPrecise(gpa, self.entries.items.len);559 try self.offsets.ensureTotalCapacityPrecise(gpa, self.entries.items.len);
349560
350 const writer = self.buffer.writer(gpa);561 const writer = self.buffer.writer(gpa);
...@@ -356,7 +567,7 @@ pub const LazyBind = struct {...@@ -356,7 +567,7 @@ pub const LazyBind = struct {
356 for (self.entries.items) |entry| {567 for (self.entries.items) |entry| {
357 self.offsets.appendAssumeCapacity(@intCast(self.buffer.items.len));568 self.offsets.appendAssumeCapacity(@intCast(self.buffer.items.len));
358569
359 const sym = ctx.getSymbol(entry.target);570 const sym = entry.target.getSymbol(ctx).?;
360 const name = sym.getName(ctx);571 const name = sym.getName(ctx);
361 const flags: u8 = if (sym.weakRef(ctx)) macho.BIND_SYMBOL_FLAGS_WEAK_IMPORT else 0;572 const flags: u8 = if (sym.weakRef(ctx)) macho.BIND_SYMBOL_FLAGS_WEAK_IMPORT else 0;
362 const ordinal: i16 = ord: {573 const ordinal: i16 = ord: {
...@@ -474,3 +685,17 @@ fn done(writer: anytype) !void {...@@ -474,3 +685,17 @@ fn done(writer: anytype) !void {
474 log.debug(">>> done", .{});685 log.debug(">>> done", .{});
475 try writer.writeByte(macho.BIND_OPCODE_DONE);686 try writer.writeByte(macho.BIND_OPCODE_DONE);
476}687}
688
689const assert = std.debug.assert;
690const leb = std.leb;
691const log = std.log.scoped(.link_dyld_info);
692const macho = std.macho;
693const mem = std.mem;
694const testing = std.testing;
695const trace = @import("../../../tracy.zig").trace;
696const std = @import("std");
697
698const Allocator = mem.Allocator;
699const File = @import("../file.zig").File;
700const MachO = @import("../../MachO.zig");
701const Symbol = @import("../Symbol.zig");
src/link/MachO/eh_frame.zig+11-6
...@@ -68,7 +68,8 @@ pub const Cie = struct {...@@ -68,7 +68,8 @@ pub const Cie = struct {
6868
69 pub fn getPersonality(cie: Cie, macho_file: *MachO) ?*Symbol {69 pub fn getPersonality(cie: Cie, macho_file: *MachO) ?*Symbol {
70 const personality = cie.personality orelse return null;70 const personality = cie.personality orelse return null;
71 return macho_file.getSymbol(personality.index);71 const object = cie.getObject(macho_file);
72 return object.getSymbolRef(personality.index, macho_file).getSymbol(macho_file);
72 }73 }
7374
74 pub fn eql(cie: Cie, other: Cie, macho_file: *MachO) bool {75 pub fn eql(cie: Cie, other: Cie, macho_file: *MachO) bool {
...@@ -223,11 +224,11 @@ pub const Fde = struct {...@@ -223,11 +224,11 @@ pub const Fde = struct {
223 }224 }
224225
225 pub fn getAtom(fde: Fde, macho_file: *MachO) *Atom {226 pub fn getAtom(fde: Fde, macho_file: *MachO) *Atom {
226 return macho_file.getAtom(fde.atom).?;227 return fde.getObject(macho_file).getAtom(fde.atom).?;
227 }228 }
228229
229 pub fn getLsdaAtom(fde: Fde, macho_file: *MachO) ?*Atom {230 pub fn getLsdaAtom(fde: Fde, macho_file: *MachO) ?*Atom {
230 return macho_file.getAtom(fde.lsda);231 return fde.getObject(macho_file).getAtom(fde.lsda);
231 }232 }
232233
233 pub fn format(234 pub fn format(
...@@ -448,7 +449,7 @@ pub fn write(macho_file: *MachO, buffer: []u8) void {...@@ -448,7 +449,7 @@ pub fn write(macho_file: *MachO, buffer: []u8) void {
448 }449 }
449}450}
450451
451pub fn writeRelocs(macho_file: *MachO, code: []u8, relocs: *std.ArrayList(macho.relocation_info)) error{Overflow}!void {452pub fn writeRelocs(macho_file: *MachO, code: []u8, relocs: []macho.relocation_info) error{Overflow}!void {
452 const tracy = trace(@src());453 const tracy = trace(@src());
453 defer tracy.end();454 defer tracy.end();
454455
...@@ -459,6 +460,7 @@ pub fn writeRelocs(macho_file: *MachO, code: []u8, relocs: *std.ArrayList(macho....@@ -459,6 +460,7 @@ pub fn writeRelocs(macho_file: *MachO, code: []u8, relocs: *std.ArrayList(macho.
459 else => 0,460 else => 0,
460 };461 };
461462
463 var i: usize = 0;
462 for (macho_file.objects.items) |index| {464 for (macho_file.objects.items) |index| {
463 const object = macho_file.getFile(index).?.object;465 const object = macho_file.getFile(index).?.object;
464 for (object.cies.items) |cie| {466 for (object.cies.items) |cie| {
...@@ -469,7 +471,7 @@ pub fn writeRelocs(macho_file: *MachO, code: []u8, relocs: *std.ArrayList(macho....@@ -469,7 +471,7 @@ pub fn writeRelocs(macho_file: *MachO, code: []u8, relocs: *std.ArrayList(macho.
469 if (cie.getPersonality(macho_file)) |sym| {471 if (cie.getPersonality(macho_file)) |sym| {
470 const r_address = math.cast(i32, cie.out_offset + cie.personality.?.offset) orelse return error.Overflow;472 const r_address = math.cast(i32, cie.out_offset + cie.personality.?.offset) orelse return error.Overflow;
471 const r_symbolnum = math.cast(u24, sym.getOutputSymtabIndex(macho_file).?) orelse return error.Overflow;473 const r_symbolnum = math.cast(u24, sym.getOutputSymtabIndex(macho_file).?) orelse return error.Overflow;
472 relocs.appendAssumeCapacity(.{474 relocs[i] = .{
473 .r_address = r_address,475 .r_address = r_address,
474 .r_symbolnum = r_symbolnum,476 .r_symbolnum = r_symbolnum,
475 .r_length = 2,477 .r_length = 2,
...@@ -480,7 +482,8 @@ pub fn writeRelocs(macho_file: *MachO, code: []u8, relocs: *std.ArrayList(macho....@@ -480,7 +482,8 @@ pub fn writeRelocs(macho_file: *MachO, code: []u8, relocs: *std.ArrayList(macho.
480 .x86_64 => @intFromEnum(macho.reloc_type_x86_64.X86_64_RELOC_GOT),482 .x86_64 => @intFromEnum(macho.reloc_type_x86_64.X86_64_RELOC_GOT),
481 else => unreachable,483 else => unreachable,
482 },484 },
483 });485 };
486 i += 1;
484 }487 }
485 }488 }
486 }489 }
...@@ -531,6 +534,8 @@ pub fn writeRelocs(macho_file: *MachO, code: []u8, relocs: *std.ArrayList(macho....@@ -531,6 +534,8 @@ pub fn writeRelocs(macho_file: *MachO, code: []u8, relocs: *std.ArrayList(macho.
531 }534 }
532 }535 }
533 }536 }
537
538 assert(relocs.len == i);
534}539}
535540
536pub const EH_PE = struct {541pub const EH_PE = struct {
src/link/MachO/file.zig+224-97
...@@ -24,41 +24,194 @@ pub const File = union(enum) {...@@ -24,41 +24,194 @@ pub const File = union(enum) {
24 _ = options;24 _ = options;
25 switch (file) {25 switch (file) {
26 .zig_object => |x| try writer.writeAll(x.path),26 .zig_object => |x| try writer.writeAll(x.path),
27 .internal => try writer.writeAll(""),27 .internal => try writer.writeAll("internal"),
28 .object => |x| try writer.print("{}", .{x.fmtPath()}),28 .object => |x| try writer.print("{}", .{x.fmtPath()}),
29 .dylib => |x| try writer.writeAll(x.path),29 .dylib => |x| try writer.writeAll(x.path),
30 }30 }
31 }31 }
3232
33 pub fn resolveSymbols(file: File, macho_file: *MachO) void {33 pub fn resolveSymbols(file: File, macho_file: *MachO) !void {
34 switch (file) {34 return switch (file) {
35 .internal => unreachable,
36 inline else => |x| x.resolveSymbols(macho_file),35 inline else => |x| x.resolveSymbols(macho_file),
37 }36 };
38 }37 }
3938
40 pub fn resetGlobals(file: File, macho_file: *MachO) void {39 pub fn scanRelocs(file: File, macho_file: *MachO) !void {
41 switch (file) {40 switch (file) {
42 .internal => unreachable,41 .dylib => unreachable,
43 inline else => |x| x.resetGlobals(macho_file),42 .internal => |x| x.scanRelocs(macho_file),
43 inline else => |x| x.scanRelocs(macho_file),
44 }
45 }
46
47 /// Encodes symbol rank so that the following ordering applies:
48 /// * strong in object
49 /// * weak in object
50 /// * tentative in object
51 /// * strong in archive/dylib
52 /// * weak in archive/dylib
53 /// * tentative in archive
54 /// * unclaimed
55 pub fn getSymbolRank(file: File, args: struct {
56 archive: bool = false,
57 weak: bool = false,
58 tentative: bool = false,
59 }) u32 {
60 if (file != .dylib and !args.archive) {
61 const base: u32 = blk: {
62 if (args.tentative) break :blk 3;
63 break :blk if (args.weak) 2 else 1;
64 };
65 return (base << 16) + file.getIndex();
66 }
67 const base: u32 = blk: {
68 if (args.tentative) break :blk 3;
69 break :blk if (args.weak) 2 else 1;
70 };
71 return base + (file.getIndex() << 24);
72 }
73
74 pub fn getAtom(file: File, atom_index: Atom.Index) ?*Atom {
75 return switch (file) {
76 .dylib => unreachable,
77 inline else => |x| x.getAtom(atom_index),
78 };
79 }
80
81 pub fn getAtoms(file: File) []const Atom.Index {
82 return switch (file) {
83 .dylib => unreachable,
84 inline else => |x| x.getAtoms(),
85 };
86 }
87
88 pub fn addAtomExtra(file: File, allocator: Allocator, extra: Atom.Extra) !u32 {
89 return switch (file) {
90 .dylib => unreachable,
91 inline else => |x| x.addAtomExtra(allocator, extra),
92 };
93 }
94
95 pub fn getAtomExtra(file: File, index: u32) Atom.Extra {
96 return switch (file) {
97 .dylib => unreachable,
98 inline else => |x| x.getAtomExtra(index),
99 };
100 }
101
102 pub fn setAtomExtra(file: File, index: u32, extra: Atom.Extra) void {
103 return switch (file) {
104 .dylib => unreachable,
105 inline else => |x| x.setAtomExtra(index, extra),
106 };
107 }
108
109 pub fn getSymbols(file: File) []Symbol {
110 return switch (file) {
111 inline else => |x| x.symbols.items,
112 };
113 }
114
115 pub fn getSymbolRef(file: File, sym_index: Symbol.Index, macho_file: *MachO) MachO.Ref {
116 return switch (file) {
117 inline else => |x| x.getSymbolRef(sym_index, macho_file),
118 };
119 }
120
121 pub fn getNlists(file: File) []macho.nlist_64 {
122 return switch (file) {
123 .dylib => unreachable,
124 .internal => |x| x.symtab.items,
125 inline else => |x| x.symtab.items(.nlist),
126 };
127 }
128
129 pub fn getGlobals(file: File) []MachO.SymbolResolver.Index {
130 return switch (file) {
131 inline else => |x| x.globals.items,
132 };
133 }
134
135 pub fn markImportsExports(file: File, macho_file: *MachO) void {
136 const tracy = trace(@src());
137 defer tracy.end();
138
139 const nsyms = switch (file) {
140 .dylib => unreachable,
141 inline else => |x| x.symbols.items.len,
142 };
143 for (0..nsyms) |i| {
144 const ref = file.getSymbolRef(@intCast(i), macho_file);
145 if (ref.getFile(macho_file) == null) continue;
146 const sym = ref.getSymbol(macho_file).?;
147 if (sym.visibility != .global) continue;
148 if (sym.getFile(macho_file).? == .dylib and !sym.flags.abs) {
149 sym.flags.import = true;
150 continue;
151 }
152 if (file.getIndex() == ref.file) {
153 sym.flags.@"export" = true;
154 }
44 }155 }
45 }156 }
46157
47 pub fn claimUnresolved(file: File, macho_file: *MachO) error{OutOfMemory}!void {158 pub fn markExportsRelocatable(file: File, macho_file: *MachO) void {
159 const tracy = trace(@src());
160 defer tracy.end();
161
48 assert(file == .object or file == .zig_object);162 assert(file == .object or file == .zig_object);
49163
50 for (file.getSymbols(), 0..) |sym_index, i| {164 for (file.getSymbols(), 0..) |*sym, i| {
51 const nlist_idx = @as(Symbol.Index, @intCast(i));165 const ref = file.getSymbolRef(@intCast(i), macho_file);
52 const nlist = switch (file) {166 const other_file = ref.getFile(macho_file) orelse continue;
53 .object => |x| x.symtab.items(.nlist)[nlist_idx],167 if (other_file.getIndex() != file.getIndex()) continue;
54 .zig_object => |x| x.symtab.items(.nlist)[nlist_idx],168 if (sym.visibility != .global) continue;
55 else => unreachable,169 sym.flags.@"export" = true;
56 };170 }
171 }
172
173 pub fn createSymbolIndirection(file: File, macho_file: *MachO) !void {
174 const tracy = trace(@src());
175 defer tracy.end();
176
177 const nsyms = switch (file) {
178 inline else => |x| x.symbols.items.len,
179 };
180 for (0..nsyms) |i| {
181 const ref = file.getSymbolRef(@intCast(i), macho_file);
182 if (ref.getFile(macho_file) == null) continue;
183 if (ref.file != file.getIndex()) continue;
184 const sym = ref.getSymbol(macho_file).?;
185 if (sym.flags.needs_got) {
186 log.debug("'{s}' needs GOT", .{sym.getName(macho_file)});
187 try macho_file.got.addSymbol(ref, macho_file);
188 }
189 if (sym.flags.stubs) {
190 log.debug("'{s}' needs STUBS", .{sym.getName(macho_file)});
191 try macho_file.stubs.addSymbol(ref, macho_file);
192 }
193 if (sym.flags.tlv_ptr) {
194 log.debug("'{s}' needs TLV pointer", .{sym.getName(macho_file)});
195 try macho_file.tlv_ptr.addSymbol(ref, macho_file);
196 }
197 if (sym.flags.objc_stubs) {
198 log.debug("'{s}' needs OBJC STUBS", .{sym.getName(macho_file)});
199 try macho_file.objc_stubs.addSymbol(ref, macho_file);
200 }
201 }
202 }
203
204 pub fn claimUnresolved(file: File, macho_file: *MachO) void {
205 const tracy = trace(@src());
206 defer tracy.end();
207
208 assert(file == .object or file == .zig_object);
209
210 for (file.getSymbols(), file.getNlists(), 0..) |*sym, nlist, i| {
57 if (!nlist.ext()) continue;211 if (!nlist.ext()) continue;
58 if (!nlist.undf()) continue;212 if (!nlist.undf()) continue;
59213
60 const sym = macho_file.getSymbol(sym_index);214 if (file.getSymbolRef(@intCast(i), macho_file).getFile(macho_file) != null) continue;
61 if (sym.getFile(macho_file) != null) continue;
62215
63 const is_import = switch (macho_file.undefined_treatment) {216 const is_import = switch (macho_file.undefined_treatment) {
64 .@"error" => false,217 .@"error" => false,
...@@ -67,111 +220,95 @@ pub const File = union(enum) {...@@ -67,111 +220,95 @@ pub const File = union(enum) {
67 };220 };
68 if (is_import) {221 if (is_import) {
69 sym.value = 0;222 sym.value = 0;
70 sym.atom = 0;223 sym.atom_ref = .{ .index = 0, .file = 0 };
71 sym.nlist_idx = 0;
72 sym.file = macho_file.internal_object.?;
73 sym.flags.weak = false;224 sym.flags.weak = false;
74 sym.flags.weak_ref = nlist.weakRef();225 sym.flags.weak_ref = nlist.weakRef();
75 sym.flags.import = is_import;226 sym.flags.import = is_import;
76 sym.visibility = .global;227 sym.visibility = .global;
77 try macho_file.getInternalObject().?.symbols.append(macho_file.base.comp.gpa, sym_index);228
229 const idx = file.getGlobals()[i];
230 macho_file.resolver.values.items[idx - 1] = .{ .index = @intCast(i), .file = file.getIndex() };
78 }231 }
79 }232 }
80 }233 }
81234
82 pub fn claimUnresolvedRelocatable(file: File, macho_file: *MachO) void {235 pub fn claimUnresolvedRelocatable(file: File, macho_file: *MachO) void {
236 const tracy = trace(@src());
237 defer tracy.end();
238
83 assert(file == .object or file == .zig_object);239 assert(file == .object or file == .zig_object);
84240
85 for (file.getSymbols(), 0..) |sym_index, i| {241 for (file.getSymbols(), file.getNlists(), 0..) |*sym, nlist, i| {
86 const nlist_idx = @as(Symbol.Index, @intCast(i));
87 const nlist = switch (file) {
88 .object => |x| x.symtab.items(.nlist)[nlist_idx],
89 .zig_object => |x| x.symtab.items(.nlist)[nlist_idx],
90 else => unreachable,
91 };
92 if (!nlist.ext()) continue;242 if (!nlist.ext()) continue;
93 if (!nlist.undf()) continue;243 if (!nlist.undf()) continue;
94244 if (file.getSymbolRef(@intCast(i), macho_file).getFile(macho_file) != null) continue;
95 const sym = macho_file.getSymbol(sym_index);
96 if (sym.getFile(macho_file) != null) continue;
97245
98 sym.value = 0;246 sym.value = 0;
99 sym.atom = 0;247 sym.atom_ref = .{ .index = 0, .file = 0 };
100 sym.nlist_idx = nlist_idx;
101 sym.file = file.getIndex();
102 sym.flags.weak_ref = nlist.weakRef();248 sym.flags.weak_ref = nlist.weakRef();
103 sym.flags.import = true;249 sym.flags.import = true;
104 sym.visibility = .global;250 sym.visibility = .global;
251
252 const idx = file.getGlobals()[i];
253 macho_file.resolver.values.items[idx - 1] = .{ .index = @intCast(i), .file = file.getIndex() };
105 }254 }
106 }255 }
107256
108 pub fn markImportsExports(file: File, macho_file: *MachO) void {257 pub fn checkDuplicates(file: File, macho_file: *MachO) !void {
109 assert(file == .object or file == .zig_object);258 const tracy = trace(@src());
259 defer tracy.end();
260
261 const gpa = macho_file.base.comp.gpa;
110262
111 for (file.getSymbols()) |sym_index| {263 for (file.getSymbols(), file.getNlists(), 0..) |sym, nlist, i| {
112 const sym = macho_file.getSymbol(sym_index);
113 const other_file = sym.getFile(macho_file) orelse continue;
114 if (sym.visibility != .global) continue;264 if (sym.visibility != .global) continue;
115 if (other_file == .dylib and !sym.flags.abs) {265 if (sym.flags.weak) continue;
116 sym.flags.import = true;266 if (nlist.undf()) continue;
117 continue;267 const ref = file.getSymbolRef(@intCast(i), macho_file);
118 }268 const ref_file = ref.getFile(macho_file) orelse continue;
119 if (other_file.getIndex() == file.getIndex()) {269 if (ref_file.getIndex() == file.getIndex()) continue;
120 sym.flags.@"export" = true;270
271 const gop = try macho_file.dupes.getOrPut(gpa, file.getGlobals()[i]);
272 if (!gop.found_existing) {
273 gop.value_ptr.* = .{};
121 }274 }
275 try gop.value_ptr.append(gpa, file.getIndex());
122 }276 }
123 }277 }
124278
125 pub fn markExportsRelocatable(file: File, macho_file: *MachO) void {279 pub fn initOutputSections(file: File, macho_file: *MachO) !void {
126 assert(file == .object or file == .zig_object);280 const tracy = trace(@src());
127281 defer tracy.end();
128 for (file.getSymbols()) |sym_index| {282 for (file.getAtoms()) |atom_index| {
129 const sym = macho_file.getSymbol(sym_index);283 const atom = file.getAtom(atom_index) orelse continue;
130 const other_file = sym.getFile(macho_file) orelse continue;284 if (!atom.flags.alive) continue;
131 if (sym.visibility != .global) continue;285 atom.out_n_sect = try Atom.initOutputSection(atom.getInputSection(macho_file), macho_file);
132 if (other_file.getIndex() == file.getIndex()) {
133 sym.flags.@"export" = true;
134 }
135 }286 }
136 }287 }
137288
138 /// Encodes symbol rank so that the following ordering applies:289 pub fn dedupLiterals(file: File, lp: MachO.LiteralPool, macho_file: *MachO) void {
139 /// * strong in object290 return switch (file) {
140 /// * weak in object291 .dylib => unreachable,
141 /// * tentative in object292 inline else => |x| x.dedupLiterals(lp, macho_file),
142 /// * strong in archive/dylib
143 /// * weak in archive/dylib
144 /// * tentative in archive
145 /// * unclaimed
146 pub fn getSymbolRank(file: File, args: struct {
147 archive: bool = false,
148 weak: bool = false,
149 tentative: bool = false,
150 }) u32 {
151 if (file == .object and !args.archive) {
152 const base: u32 = blk: {
153 if (args.tentative) break :blk 3;
154 break :blk if (args.weak) 2 else 1;
155 };
156 return (base << 16) + file.getIndex();
157 }
158 const base: u32 = blk: {
159 if (args.tentative) break :blk 3;
160 break :blk if (args.weak) 2 else 1;
161 };293 };
162 return base + (file.getIndex() << 24);
163 }294 }
164295
165 pub fn getSymbols(file: File) []const Symbol.Index {296 pub fn writeAtoms(file: File, macho_file: *MachO) !void {
166 return switch (file) {297 return switch (file) {
167 inline else => |x| x.symbols.items,298 .dylib, .zig_object => unreachable,
299 inline else => |x| x.writeAtoms(macho_file),
168 };300 };
169 }301 }
170302
171 pub fn getAtoms(file: File) []const Atom.Index {303 pub fn calcSymtabSize(file: File, macho_file: *MachO) void {
172 return switch (file) {304 return switch (file) {
173 .dylib => unreachable,305 inline else => |x| x.calcSymtabSize(macho_file),
174 inline else => |x| x.atoms.items,306 };
307 }
308
309 pub fn writeSymtab(file: File, macho_file: *MachO, ctx: anytype) void {
310 return switch (file) {
311 inline else => |x| x.writeSymtab(macho_file, ctx),
175 };312 };
176 }313 }
177314
...@@ -198,18 +335,6 @@ pub const File = union(enum) {...@@ -198,18 +335,6 @@ pub const File = union(enum) {
198 };335 };
199 }336 }
200337
201 pub fn calcSymtabSize(file: File, macho_file: *MachO) !void {
202 return switch (file) {
203 inline else => |x| x.calcSymtabSize(macho_file),
204 };
205 }
206
207 pub fn writeSymtab(file: File, macho_file: *MachO, ctx: anytype) !void {
208 return switch (file) {
209 inline else => |x| x.writeSymtab(macho_file, ctx),
210 };
211 }
212
213 pub const Index = u32;338 pub const Index = u32;
214339
215 pub const Entry = union(enum) {340 pub const Entry = union(enum) {
...@@ -225,8 +350,10 @@ pub const File = union(enum) {...@@ -225,8 +350,10 @@ pub const File = union(enum) {
225};350};
226351
227const assert = std.debug.assert;352const assert = std.debug.assert;
353const log = std.log.scoped(.link);
228const macho = std.macho;354const macho = std.macho;
229const std = @import("std");355const std = @import("std");
356const trace = @import("../../tracy.zig").trace;
230357
231const Allocator = std.mem.Allocator;358const Allocator = std.mem.Allocator;
232const Archive = @import("Archive.zig");359const Archive = @import("Archive.zig");
src/link/MachO/relocatable.zig+222-286
...@@ -44,9 +44,7 @@ pub fn flushObject(macho_file: *MachO, comp: *Compilation, module_obj_path: ?[]c...@@ -44,9 +44,7 @@ pub fn flushObject(macho_file: *MachO, comp: *Compilation, module_obj_path: ?[]c
4444
45 if (comp.link_errors.items.len > 0) return error.FlushFailure;45 if (comp.link_errors.items.len > 0) return error.FlushFailure;
4646
47 try macho_file.addUndefinedGlobals();
48 try macho_file.resolveSymbols();47 try macho_file.resolveSymbols();
49 try macho_file.parseDebugInfo();
50 try macho_file.dedupLiterals();48 try macho_file.dedupLiterals();
51 markExports(macho_file);49 markExports(macho_file);
52 claimUnresolved(macho_file);50 claimUnresolved(macho_file);
...@@ -59,28 +57,13 @@ pub fn flushObject(macho_file: *MachO, comp: *Compilation, module_obj_path: ?[]c...@@ -59,28 +57,13 @@ pub fn flushObject(macho_file: *MachO, comp: *Compilation, module_obj_path: ?[]c
59 try allocateSections(macho_file);57 try allocateSections(macho_file);
60 allocateSegment(macho_file);58 allocateSegment(macho_file);
6159
62 var off = off: {
63 const seg = macho_file.segments.items[0];
64 const off = math.cast(u32, seg.fileoff + seg.filesize) orelse return error.Overflow;
65 break :off mem.alignForward(u32, off, @alignOf(macho.relocation_info));
66 };
67 off = allocateSectionsRelocs(macho_file, off);
68
69 if (build_options.enable_logging) {60 if (build_options.enable_logging) {
70 state_log.debug("{}", .{macho_file.dumpState()});61 state_log.debug("{}", .{macho_file.dumpState()});
71 }62 }
7263
73 try macho_file.calcSymtabSize();64 try writeSections(macho_file);
74 try writeAtoms(macho_file);65 sortRelocs(macho_file);
75 try writeCompactUnwind(macho_file);66 try writeSectionsToFile(macho_file);
76 try writeEhFrame(macho_file);
77
78 off = mem.alignForward(u32, off, @alignOf(u64));
79 off = try macho_file.writeDataInCode(0, off);
80 off = mem.alignForward(u32, off, @alignOf(u64));
81 off = try macho_file.writeSymtab(off);
82 off = mem.alignForward(u32, off, @alignOf(u64));
83 off = try macho_file.writeStrtab(off);
8467
85 // In order to please Apple ld (and possibly other MachO linkers in the wild),68 // In order to please Apple ld (and possibly other MachO linkers in the wild),
86 // we will now sanitize segment names of Zig-specific segments.69 // we will now sanitize segment names of Zig-specific segments.
...@@ -129,7 +112,7 @@ pub fn flushStaticLib(macho_file: *MachO, comp: *Compilation, module_obj_path: ?...@@ -129,7 +112,7 @@ pub fn flushStaticLib(macho_file: *MachO, comp: *Compilation, module_obj_path: ?
129112
130 // First, we flush relocatable object file generated with our backends.113 // First, we flush relocatable object file generated with our backends.
131 if (macho_file.getZigObject()) |zo| {114 if (macho_file.getZigObject()) |zo| {
132 zo.resolveSymbols(macho_file);115 try zo.resolveSymbols(macho_file);
133 zo.asFile().markExportsRelocatable(macho_file);116 zo.asFile().markExportsRelocatable(macho_file);
134 zo.asFile().claimUnresolvedRelocatable(macho_file);117 zo.asFile().claimUnresolvedRelocatable(macho_file);
135 try macho_file.sortSections();118 try macho_file.sortSections();
...@@ -139,26 +122,13 @@ pub fn flushStaticLib(macho_file: *MachO, comp: *Compilation, module_obj_path: ?...@@ -139,26 +122,13 @@ pub fn flushStaticLib(macho_file: *MachO, comp: *Compilation, module_obj_path: ?
139 try allocateSections(macho_file);122 try allocateSections(macho_file);
140 allocateSegment(macho_file);123 allocateSegment(macho_file);
141124
142 var off = off: {
143 const seg = macho_file.segments.items[0];
144 const off = math.cast(u32, seg.fileoff + seg.filesize) orelse return error.Overflow;
145 break :off mem.alignForward(u32, off, @alignOf(macho.relocation_info));
146 };
147 off = allocateSectionsRelocs(macho_file, off);
148
149 if (build_options.enable_logging) {125 if (build_options.enable_logging) {
150 state_log.debug("{}", .{macho_file.dumpState()});126 state_log.debug("{}", .{macho_file.dumpState()});
151 }127 }
152128
153 try macho_file.calcSymtabSize();129 try writeSections(macho_file);
154 try writeAtoms(macho_file);130 sortRelocs(macho_file);
155131 try writeSectionsToFile(macho_file);
156 off = mem.alignForward(u32, off, @alignOf(u64));
157 off = try macho_file.writeDataInCode(0, off);
158 off = mem.alignForward(u32, off, @alignOf(u64));
159 off = try macho_file.writeSymtab(off);
160 off = mem.alignForward(u32, off, @alignOf(u64));
161 off = try macho_file.writeStrtab(off);
162132
163 // In order to please Apple ld (and possibly other MachO linkers in the wild),133 // In order to please Apple ld (and possibly other MachO linkers in the wild),
164 // we will now sanitize segment names of Zig-specific segments.134 // we will now sanitize segment names of Zig-specific segments.
...@@ -169,7 +139,7 @@ pub fn flushStaticLib(macho_file: *MachO, comp: *Compilation, module_obj_path: ?...@@ -169,7 +139,7 @@ pub fn flushStaticLib(macho_file: *MachO, comp: *Compilation, module_obj_path: ?
169139
170 // TODO we can avoid reading in the file contents we just wrote if we give the linker140 // TODO we can avoid reading in the file contents we just wrote if we give the linker
171 // ability to write directly to a buffer.141 // ability to write directly to a buffer.
172 try zo.readFileContents(off, macho_file);142 try zo.readFileContents(macho_file);
173 }143 }
174144
175 var files = std.ArrayList(File.Index).init(gpa);145 var files = std.ArrayList(File.Index).init(gpa);
...@@ -286,12 +256,15 @@ fn parseObject(macho_file: *MachO, path: []const u8) MachO.ParseError!void {...@@ -286,12 +256,15 @@ fn parseObject(macho_file: *MachO, path: []const u8) MachO.ParseError!void {
286 break :mtime @as(u64, @intCast(@divFloor(stat.mtime, 1_000_000_000)));256 break :mtime @as(u64, @intCast(@divFloor(stat.mtime, 1_000_000_000)));
287 };257 };
288 const index = @as(File.Index, @intCast(try macho_file.files.addOne(gpa)));258 const index = @as(File.Index, @intCast(try macho_file.files.addOne(gpa)));
289 macho_file.files.set(index, .{ .object = .{259 macho_file.files.set(index, .{
290 .path = try gpa.dupe(u8, path),260 .object = .{
291 .file_handle = handle,261 .offset = 0, // TODO FAT objects
292 .mtime = mtime,262 .path = try gpa.dupe(u8, path),
293 .index = index,263 .file_handle = handle,
294 } });264 .mtime = mtime,
265 .index = index,
266 },
267 });
295 try macho_file.objects.append(gpa, index);268 try macho_file.objects.append(gpa, index);
296269
297 const object = macho_file.getFile(index).?.object;270 const object = macho_file.getFile(index).?.object;
...@@ -347,9 +320,9 @@ pub fn claimUnresolved(macho_file: *MachO) void {...@@ -347,9 +320,9 @@ pub fn claimUnresolved(macho_file: *MachO) void {
347320
348fn initOutputSections(macho_file: *MachO) !void {321fn initOutputSections(macho_file: *MachO) !void {
349 for (macho_file.objects.items) |index| {322 for (macho_file.objects.items) |index| {
350 const object = macho_file.getFile(index).?.object;323 const file = macho_file.getFile(index).?;
351 for (object.atoms.items) |atom_index| {324 for (file.getAtoms()) |atom_index| {
352 const atom = macho_file.getAtom(atom_index) orelse continue;325 const atom = file.getAtom(atom_index) orelse continue;
353 if (!atom.flags.alive) continue;326 if (!atom.flags.alive) continue;
354 atom.out_n_sect = try Atom.initOutputSection(atom.getInputSection(macho_file), macho_file);327 atom.out_n_sect = try Atom.initOutputSection(atom.getInputSection(macho_file), macho_file);
355 }328 }
...@@ -377,69 +350,147 @@ fn calcSectionSizes(macho_file: *MachO) !void {...@@ -377,69 +350,147 @@ fn calcSectionSizes(macho_file: *MachO) !void {
377 const tracy = trace(@src());350 const tracy = trace(@src());
378 defer tracy.end();351 defer tracy.end();
379352
380 const slice = macho_file.sections.slice();353 for (macho_file.sections.items(.atoms), 0..) |atoms, i| {
381 for (slice.items(.header), slice.items(.atoms)) |*header, atoms| {
382 if (atoms.items.len == 0) continue;354 if (atoms.items.len == 0) continue;
383 for (atoms.items) |atom_index| {355 calcSectionSize(macho_file, @intCast(i));
384 const atom = macho_file.getAtom(atom_index).?;
385 const atom_alignment = atom.alignment.toByteUnits() orelse 1;
386 const offset = mem.alignForward(u64, header.size, atom_alignment);
387 const padding = offset - header.size;
388 atom.value = offset;
389 header.size += padding + atom.size;
390 header.@"align" = @max(header.@"align", atom.alignment.toLog2Units());
391 header.nreloc += atom.calcNumRelocs(macho_file);
392 }
393 }356 }
394357
395 if (macho_file.unwind_info_sect_index) |index| {358 if (macho_file.getZigObject()) |zo| {
396 calcCompactUnwindSize(macho_file, index);359 // TODO this will create a race
360 zo.calcNumRelocs(macho_file);
361 zo.calcSymtabSize(macho_file);
397 }362 }
398363
399 if (macho_file.eh_frame_sect_index) |index| {364 if (macho_file.eh_frame_sect_index) |_| {
400 const sect = &macho_file.sections.items(.header)[index];365 try calcEhFrameSize(macho_file);
401 sect.size = try eh_frame.calcSize(macho_file);
402 sect.@"align" = 3;
403 sect.nreloc = eh_frame.calcNumRelocs(macho_file);
404 }366 }
405367
406 if (macho_file.getZigObject()) |zo| {368 for (macho_file.objects.items) |index| {
407 for (zo.atoms.items) |atom_index| {369 if (macho_file.unwind_info_sect_index) |_| {
408 const atom = macho_file.getAtom(atom_index) orelse continue;370 macho_file.getFile(index).?.object.calcCompactUnwindSizeRelocatable(macho_file);
409 if (!atom.flags.alive) continue;
410 const header = &macho_file.sections.items(.header)[atom.out_n_sect];
411 if (!macho_file.isZigSection(atom.out_n_sect) and !macho_file.isDebugSection(atom.out_n_sect)) continue;
412 header.nreloc += atom.calcNumRelocs(macho_file);
413 }371 }
372 macho_file.getFile(index).?.calcSymtabSize(macho_file);
414 }373 }
374
375 try macho_file.data_in_code.updateSize(macho_file);
376
377 if (macho_file.unwind_info_sect_index) |_| {
378 calcCompactUnwindSize(macho_file);
379 }
380 try calcSymtabSize(macho_file);
381}
382
383fn calcSectionSize(macho_file: *MachO, sect_id: u8) void {
384 const tracy = trace(@src());
385 defer tracy.end();
386
387 const slice = macho_file.sections.slice();
388 const header = &slice.items(.header)[sect_id];
389 const atoms = slice.items(.atoms)[sect_id].items;
390 for (atoms) |ref| {
391 const atom = ref.getAtom(macho_file).?;
392 const atom_alignment = atom.alignment.toByteUnits() orelse 1;
393 const offset = mem.alignForward(u64, header.size, atom_alignment);
394 const padding = offset - header.size;
395 atom.value = offset;
396 header.size += padding + atom.size;
397 header.@"align" = @max(header.@"align", atom.alignment.toLog2Units());
398 const nreloc = atom.calcNumRelocs(macho_file);
399 atom.addExtra(.{ .rel_out_index = header.nreloc, .rel_out_count = nreloc }, macho_file);
400 header.nreloc += nreloc;
401 }
402}
403
404fn calcEhFrameSize(macho_file: *MachO) !void {
405 const tracy = trace(@src());
406 defer tracy.end();
407
408 const header = &macho_file.sections.items(.header)[macho_file.eh_frame_sect_index.?];
409 header.size = try eh_frame.calcSize(macho_file);
410 header.@"align" = 3;
411 header.nreloc = eh_frame.calcNumRelocs(macho_file);
415}412}
416413
417fn calcCompactUnwindSize(macho_file: *MachO, sect_index: u8) void {414fn calcCompactUnwindSize(macho_file: *MachO) void {
418 var size: u32 = 0;415 const tracy = trace(@src());
416 defer tracy.end();
417
418 var nrec: u32 = 0;
419 var nreloc: u32 = 0;419 var nreloc: u32 = 0;
420420
421 for (macho_file.objects.items) |index| {421 for (macho_file.objects.items) |index| {
422 const object = macho_file.getFile(index).?.object;422 const ctx = &macho_file.getFile(index).?.object.compact_unwind_ctx;
423 for (object.unwind_records.items) |irec| {423 ctx.rec_index = nrec;
424 const rec = macho_file.getUnwindRecord(irec);424 ctx.reloc_index = nreloc;
425 if (!rec.alive) continue;425 nrec += ctx.rec_count;
426 size += @sizeOf(macho.compact_unwind_entry);426 nreloc += ctx.reloc_count;
427 nreloc += 1;
428 if (rec.getPersonality(macho_file)) |_| {
429 nreloc += 1;
430 }
431 if (rec.getLsdaAtom(macho_file)) |_| {
432 nreloc += 1;
433 }
434 }
435 }427 }
436428
437 const sect = &macho_file.sections.items(.header)[sect_index];429 const sect = &macho_file.sections.items(.header)[macho_file.unwind_info_sect_index.?];
438 sect.size = size;430 sect.size = nrec * @sizeOf(macho.compact_unwind_entry);
439 sect.nreloc = nreloc;431 sect.nreloc = nreloc;
440 sect.@"align" = 3;432 sect.@"align" = 3;
441}433}
442434
435fn calcSymtabSize(macho_file: *MachO) error{OutOfMemory}!void {
436 const tracy = trace(@src());
437 defer tracy.end();
438
439 const gpa = macho_file.base.comp.gpa;
440
441 var nlocals: u32 = 0;
442 var nstabs: u32 = 0;
443 var nexports: u32 = 0;
444 var nimports: u32 = 0;
445 var strsize: u32 = 1;
446
447 var objects = try std.ArrayList(File.Index).initCapacity(gpa, macho_file.objects.items.len + 1);
448 defer objects.deinit();
449 if (macho_file.getZigObject()) |zo| objects.appendAssumeCapacity(zo.index);
450 objects.appendSliceAssumeCapacity(macho_file.objects.items);
451
452 for (objects.items) |index| {
453 const ctx = switch (macho_file.getFile(index).?) {
454 inline else => |x| &x.output_symtab_ctx,
455 };
456 ctx.ilocal = nlocals;
457 ctx.istab = nstabs;
458 ctx.iexport = nexports;
459 ctx.iimport = nimports;
460 ctx.stroff = strsize;
461 nlocals += ctx.nlocals;
462 nstabs += ctx.nstabs;
463 nexports += ctx.nexports;
464 nimports += ctx.nimports;
465 strsize += ctx.strsize;
466 }
467
468 for (objects.items) |index| {
469 const ctx = switch (macho_file.getFile(index).?) {
470 inline else => |x| &x.output_symtab_ctx,
471 };
472 ctx.istab += nlocals;
473 ctx.iexport += nlocals + nstabs;
474 ctx.iimport += nlocals + nstabs + nexports;
475 }
476
477 {
478 const cmd = &macho_file.symtab_cmd;
479 cmd.nsyms = nlocals + nstabs + nexports + nimports;
480 cmd.strsize = strsize;
481 }
482
483 {
484 const cmd = &macho_file.dysymtab_cmd;
485 cmd.ilocalsym = 0;
486 cmd.nlocalsym = nlocals + nstabs;
487 cmd.iextdefsym = nlocals + nstabs;
488 cmd.nextdefsym = nexports;
489 cmd.iundefsym = nlocals + nstabs + nexports;
490 cmd.nundefsym = nimports;
491 }
492}
493
443fn allocateSections(macho_file: *MachO) !void {494fn allocateSections(macho_file: *MachO) !void {
444 const slice = macho_file.sections.slice();495 const slice = macho_file.sections.slice();
445 for (slice.items(.header)) |*header| {496 for (slice.items(.header)) |*header| {
...@@ -457,6 +508,37 @@ fn allocateSections(macho_file: *MachO) !void {...@@ -457,6 +508,37 @@ fn allocateSections(macho_file: *MachO) !void {
457 }508 }
458 header.size = needed_size;509 header.size = needed_size;
459 }510 }
511
512 var fileoff: u32 = 0;
513 for (slice.items(.header)) |header| {
514 fileoff = @max(fileoff, header.offset + @as(u32, @intCast(header.size)));
515 }
516
517 for (slice.items(.header)) |*header| {
518 if (header.nreloc == 0) continue;
519 header.reloff = mem.alignForward(u32, fileoff, @alignOf(macho.relocation_info));
520 fileoff = header.reloff + header.nreloc * @sizeOf(macho.relocation_info);
521 }
522
523 // In -r mode, there is no LINKEDIT segment and so we allocate required LINKEDIT commands
524 // as if they were detached or part of the single segment.
525
526 // DATA_IN_CODE
527 {
528 const cmd = &macho_file.data_in_code_cmd;
529 cmd.dataoff = fileoff;
530 fileoff += cmd.datasize;
531 fileoff = mem.alignForward(u32, fileoff, @alignOf(u64));
532 }
533
534 // SYMTAB
535 {
536 const cmd = &macho_file.symtab_cmd;
537 cmd.symoff = fileoff;
538 fileoff += cmd.nsyms * @sizeOf(macho.nlist_64);
539 fileoff = mem.alignForward(u32, fileoff, @alignOf(u32));
540 cmd.stroff = fileoff;
541 }
460}542}
461543
462/// Renames segment names in Zig sections to standard MachO segment names such as544/// Renames segment names in Zig sections to standard MachO segment names such as
...@@ -519,232 +601,86 @@ fn allocateSegment(macho_file: *MachO) void {...@@ -519,232 +601,86 @@ fn allocateSegment(macho_file: *MachO) void {
519 seg.filesize = fileoff - seg.fileoff;601 seg.filesize = fileoff - seg.fileoff;
520}602}
521603
522fn allocateSectionsRelocs(macho_file: *MachO, off: u32) u32 {
523 var fileoff = off;
524 const slice = macho_file.sections.slice();
525 for (slice.items(.header)) |*header| {
526 if (header.nreloc == 0) continue;
527 header.reloff = mem.alignForward(u32, fileoff, @alignOf(macho.relocation_info));
528 fileoff = header.reloff + header.nreloc * @sizeOf(macho.relocation_info);
529 }
530 return fileoff;
531}
532
533// We need to sort relocations in descending order to be compatible with Apple's linker.604// We need to sort relocations in descending order to be compatible with Apple's linker.
534fn sortReloc(ctx: void, lhs: macho.relocation_info, rhs: macho.relocation_info) bool {605fn sortReloc(ctx: void, lhs: macho.relocation_info, rhs: macho.relocation_info) bool {
535 _ = ctx;606 _ = ctx;
536 return lhs.r_address > rhs.r_address;607 return lhs.r_address > rhs.r_address;
537}608}
538609
539fn writeAtoms(macho_file: *MachO) !void {610fn sortRelocs(macho_file: *MachO) void {
611 const tracy = trace(@src());
612 defer tracy.end();
613
614 for (macho_file.sections.items(.relocs)) |*relocs| {
615 mem.sort(macho.relocation_info, relocs.items, {}, sortReloc);
616 }
617}
618
619fn writeSections(macho_file: *MachO) !void {
540 const tracy = trace(@src());620 const tracy = trace(@src());
541 defer tracy.end();621 defer tracy.end();
542622
543 const gpa = macho_file.base.comp.gpa;623 const gpa = macho_file.base.comp.gpa;
544 const cpu_arch = macho_file.getTarget().cpu.arch;624 const cpu_arch = macho_file.getTarget().cpu.arch;
545 const slice = macho_file.sections.slice();625 const slice = macho_file.sections.slice();
546626 for (slice.items(.header), slice.items(.out), slice.items(.relocs), 0..) |header, *out, *relocs, n_sect| {
547 var relocs = std.ArrayList(macho.relocation_info).init(gpa);
548 defer relocs.deinit();
549
550 for (slice.items(.header), slice.items(.atoms), 0..) |header, atoms, i| {
551 if (atoms.items.len == 0) continue;
552 if (header.isZerofill()) continue;627 if (header.isZerofill()) continue;
553 if (macho_file.isZigSection(@intCast(i)) or macho_file.isDebugSection(@intCast(i))) continue;628 if (!macho_file.isZigSection(@intCast(n_sect))) { // TODO this is wrong; what about debug sections?
554629 const size = math.cast(usize, header.size) orelse return error.Overflow;
555 const size = math.cast(usize, header.size) orelse return error.Overflow;630 try out.resize(gpa, size);
556 const code = try gpa.alloc(u8, size);631 const padding_byte: u8 = if (header.isCode() and cpu_arch == .x86_64) 0xcc else 0;
557 defer gpa.free(code);632 @memset(out.items, padding_byte);
558 const padding_byte: u8 = if (header.isCode() and cpu_arch == .x86_64) 0xcc else 0;
559 @memset(code, padding_byte);
560
561 try relocs.ensureTotalCapacity(header.nreloc);
562
563 for (atoms.items) |atom_index| {
564 const atom = macho_file.getAtom(atom_index).?;
565 assert(atom.flags.alive);
566 const off = math.cast(usize, atom.value) orelse return error.Overflow;
567 const atom_size = math.cast(usize, atom.size) orelse return error.Overflow;
568 try atom.getData(macho_file, code[off..][0..atom_size]);
569 try atom.writeRelocs(macho_file, code[off..][0..atom_size], &relocs);
570 }633 }
634 try relocs.resize(gpa, header.nreloc);
635 }
571636
572 assert(relocs.items.len == header.nreloc);637 const cmd = macho_file.symtab_cmd;
573638 try macho_file.symtab.resize(gpa, cmd.nsyms);
574 mem.sort(macho.relocation_info, relocs.items, {}, sortReloc);639 try macho_file.strtab.resize(gpa, cmd.strsize);
575640 macho_file.strtab.items[0] = 0;
576 // TODO scattered writes?
577 try macho_file.base.file.?.pwriteAll(code, header.offset);
578 try macho_file.base.file.?.pwriteAll(mem.sliceAsBytes(relocs.items), header.reloff);
579641
580 relocs.clearRetainingCapacity();642 for (macho_file.objects.items) |index| {
643 try macho_file.getFile(index).?.object.writeAtomsRelocatable(macho_file);
644 macho_file.getFile(index).?.writeSymtab(macho_file, macho_file);
581 }645 }
582646
583 if (macho_file.getZigObject()) |zo| {647 if (macho_file.getZigObject()) |zo| {
584 // TODO: this is ugly; perhaps we should aggregrate before?648 try zo.writeRelocs(macho_file);
585 var zo_relocs = std.AutoArrayHashMap(u8, std.ArrayList(macho.relocation_info)).init(gpa);649 try zo.writeAtomsRelocatable(macho_file);
586 defer {650 zo.writeSymtab(macho_file, macho_file);
587 for (zo_relocs.values()) |*list| {
588 list.deinit();
589 }
590 zo_relocs.deinit();
591 }
592
593 for (macho_file.sections.items(.header), 0..) |header, n_sect| {
594 if (header.isZerofill()) continue;
595 if (!macho_file.isZigSection(@intCast(n_sect)) and !macho_file.isDebugSection(@intCast(n_sect))) continue;
596 const gop = try zo_relocs.getOrPut(@intCast(n_sect));
597 if (gop.found_existing) continue;
598 gop.value_ptr.* = try std.ArrayList(macho.relocation_info).initCapacity(gpa, header.nreloc);
599 }
600
601 for (zo.atoms.items) |atom_index| {
602 const atom = macho_file.getAtom(atom_index) orelse continue;
603 if (!atom.flags.alive) continue;
604 const header = macho_file.sections.items(.header)[atom.out_n_sect];
605 if (header.isZerofill()) continue;
606 if (!macho_file.isZigSection(atom.out_n_sect) and !macho_file.isDebugSection(atom.out_n_sect)) continue;
607 if (atom.getRelocs(macho_file).len == 0) continue;
608 const atom_size = math.cast(usize, atom.size) orelse return error.Overflow;
609 const code = try gpa.alloc(u8, atom_size);
610 defer gpa.free(code);
611 atom.getData(macho_file, code) catch |err| switch (err) {
612 error.InputOutput => {
613 try macho_file.reportUnexpectedError("fetching code for '{s}' failed", .{
614 atom.getName(macho_file),
615 });
616 return error.FlushFailure;
617 },
618 else => |e| {
619 try macho_file.reportUnexpectedError("unexpected error while fetching code for '{s}': {s}", .{
620 atom.getName(macho_file),
621 @errorName(e),
622 });
623 return error.FlushFailure;
624 },
625 };
626 const file_offset = header.offset + atom.value;
627 const rels = zo_relocs.getPtr(atom.out_n_sect).?;
628 try atom.writeRelocs(macho_file, code, rels);
629 try macho_file.base.file.?.pwriteAll(code, file_offset);
630 }
631
632 for (zo_relocs.keys(), zo_relocs.values()) |sect_id, rels| {
633 const header = macho_file.sections.items(.header)[sect_id];
634 assert(rels.items.len == header.nreloc);
635 mem.sort(macho.relocation_info, rels.items, {}, sortReloc);
636 try macho_file.base.file.?.pwriteAll(mem.sliceAsBytes(rels.items), header.reloff);
637 }
638 }651 }
639}
640
641fn writeCompactUnwind(macho_file: *MachO) !void {
642 const sect_index = macho_file.unwind_info_sect_index orelse return;
643 const gpa = macho_file.base.comp.gpa;
644 const header = macho_file.sections.items(.header)[sect_index];
645
646 const nrecs = math.cast(usize, @divExact(header.size, @sizeOf(macho.compact_unwind_entry))) orelse return error.Overflow;
647 var entries = try std.ArrayList(macho.compact_unwind_entry).initCapacity(gpa, nrecs);
648 defer entries.deinit();
649
650 var relocs = try std.ArrayList(macho.relocation_info).initCapacity(gpa, header.nreloc);
651 defer relocs.deinit();
652
653 const addReloc = struct {
654 fn addReloc(offset: i32, cpu_arch: std.Target.Cpu.Arch) macho.relocation_info {
655 return .{
656 .r_address = offset,
657 .r_symbolnum = 0,
658 .r_pcrel = 0,
659 .r_length = 3,
660 .r_extern = 0,
661 .r_type = switch (cpu_arch) {
662 .aarch64 => @intFromEnum(macho.reloc_type_arm64.ARM64_RELOC_UNSIGNED),
663 .x86_64 => @intFromEnum(macho.reloc_type_x86_64.X86_64_RELOC_UNSIGNED),
664 else => unreachable,
665 },
666 };
667 }
668 }.addReloc;
669
670 var offset: i32 = 0;
671 for (macho_file.objects.items) |index| {
672 const object = macho_file.getFile(index).?.object;
673 for (object.unwind_records.items) |irec| {
674 const rec = macho_file.getUnwindRecord(irec);
675 if (!rec.alive) continue;
676
677 var out: macho.compact_unwind_entry = .{
678 .rangeStart = 0,
679 .rangeLength = rec.length,
680 .compactUnwindEncoding = rec.enc.enc,
681 .personalityFunction = 0,
682 .lsda = 0,
683 };
684
685 {
686 // Function address
687 const atom = rec.getAtom(macho_file);
688 const addr = rec.getAtomAddress(macho_file);
689 out.rangeStart = addr;
690 var reloc = addReloc(offset, macho_file.getTarget().cpu.arch);
691 reloc.r_symbolnum = atom.out_n_sect + 1;
692 relocs.appendAssumeCapacity(reloc);
693 }
694652
695 // Personality function653 if (macho_file.eh_frame_sect_index) |_| {
696 if (rec.getPersonality(macho_file)) |sym| {654 try writeEhFrame(macho_file);
697 const r_symbolnum = math.cast(u24, sym.getOutputSymtabIndex(macho_file).?) orelse return error.Overflow;655 }
698 var reloc = addReloc(offset + 16, macho_file.getTarget().cpu.arch);
699 reloc.r_symbolnum = r_symbolnum;
700 reloc.r_extern = 1;
701 relocs.appendAssumeCapacity(reloc);
702 }
703
704 // LSDA address
705 if (rec.getLsdaAtom(macho_file)) |atom| {
706 const addr = rec.getLsdaAddress(macho_file);
707 out.lsda = addr;
708 var reloc = addReloc(offset + 24, macho_file.getTarget().cpu.arch);
709 reloc.r_symbolnum = atom.out_n_sect + 1;
710 relocs.appendAssumeCapacity(reloc);
711 }
712656
713 entries.appendAssumeCapacity(out);657 if (macho_file.unwind_info_sect_index) |_| {
714 offset += @sizeOf(macho.compact_unwind_entry);658 for (macho_file.objects.items) |index| {
659 try macho_file.getFile(index).?.object.writeCompactUnwindRelocatable(macho_file);
715 }660 }
716 }661 }
717
718 assert(entries.items.len == nrecs);
719 assert(relocs.items.len == header.nreloc);
720
721 mem.sort(macho.relocation_info, relocs.items, {}, sortReloc);
722
723 // TODO scattered writes?
724 try macho_file.base.file.?.pwriteAll(mem.sliceAsBytes(entries.items), header.offset);
725 try macho_file.base.file.?.pwriteAll(mem.sliceAsBytes(relocs.items), header.reloff);
726}662}
727663
728fn writeEhFrame(macho_file: *MachO) !void {664fn writeEhFrame(macho_file: *MachO) !void {
729 const sect_index = macho_file.eh_frame_sect_index orelse return;665 const sect_index = macho_file.eh_frame_sect_index.?;
730 const gpa = macho_file.base.comp.gpa;666 const buffer = macho_file.sections.items(.out)[sect_index];
731 const header = macho_file.sections.items(.header)[sect_index];667 const relocs = macho_file.sections.items(.relocs)[sect_index];
732 const size = math.cast(usize, header.size) orelse return error.Overflow;668 try eh_frame.writeRelocs(macho_file, buffer.items, relocs.items);
733669}
734 const code = try gpa.alloc(u8, size);
735 defer gpa.free(code);
736
737 var relocs = try std.ArrayList(macho.relocation_info).initCapacity(gpa, header.nreloc);
738 defer relocs.deinit();
739670
740 try eh_frame.writeRelocs(macho_file, code, &relocs);671fn writeSectionsToFile(macho_file: *MachO) !void {
741 assert(relocs.items.len == header.nreloc);672 const tracy = trace(@src());
673 defer tracy.end();
742674
743 mem.sort(macho.relocation_info, relocs.items, {}, sortReloc);675 const slice = macho_file.sections.slice();
676 for (slice.items(.header), slice.items(.out), slice.items(.relocs)) |header, out, relocs| {
677 try macho_file.base.file.?.pwriteAll(out.items, header.offset);
678 try macho_file.base.file.?.pwriteAll(mem.sliceAsBytes(relocs.items), header.reloff);
679 }
744680
745 // TODO scattered writes?681 try macho_file.writeDataInCode();
746 try macho_file.base.file.?.pwriteAll(code, header.offset);682 try macho_file.base.file.?.pwriteAll(mem.sliceAsBytes(macho_file.symtab.items), macho_file.symtab_cmd.symoff);
747 try macho_file.base.file.?.pwriteAll(mem.sliceAsBytes(relocs.items), header.reloff);683 try macho_file.base.file.?.pwriteAll(macho_file.strtab.items, macho_file.symtab_cmd.stroff);
748}684}
749685
750fn writeLoadCommands(macho_file: *MachO) !struct { usize, usize } {686fn writeLoadCommands(macho_file: *MachO) !struct { usize, usize } {
src/link/MachO/synthetic.zig+151-204
...@@ -18,15 +18,15 @@ pub const ZigGotSection = struct {...@@ -18,15 +18,15 @@ pub const ZigGotSection = struct {
18 }18 }
1919
20 pub fn addSymbol(zig_got: *ZigGotSection, sym_index: Symbol.Index, macho_file: *MachO) !Index {20 pub fn addSymbol(zig_got: *ZigGotSection, sym_index: Symbol.Index, macho_file: *MachO) !Index {
21 const comp = macho_file.base.comp;21 const gpa = macho_file.base.comp.gpa;
22 const gpa = comp.gpa;22 const zo = macho_file.getZigObject().?;
23 const index = try zig_got.allocateEntry(gpa);23 const index = try zig_got.allocateEntry(gpa);
24 const entry = &zig_got.entries.items[index];24 const entry = &zig_got.entries.items[index];
25 entry.* = sym_index;25 entry.* = sym_index;
26 const symbol = macho_file.getSymbol(sym_index);26 const symbol = &zo.symbols.items[sym_index];
27 assert(symbol.flags.needs_zig_got);27 assert(symbol.flags.needs_zig_got);
28 symbol.flags.has_zig_got = true;28 symbol.flags.has_zig_got = true;
29 try symbol.addExtra(.{ .zig_got = index }, macho_file);29 symbol.addExtra(.{ .zig_got = index }, macho_file);
30 return index;30 return index;
31 }31 }
3232
...@@ -53,9 +53,10 @@ pub const ZigGotSection = struct {...@@ -53,9 +53,10 @@ pub const ZigGotSection = struct {
53 try macho_file.growSection(macho_file.zig_got_sect_index.?, needed_size);53 try macho_file.growSection(macho_file.zig_got_sect_index.?, needed_size);
54 zig_got.dirty = false;54 zig_got.dirty = false;
55 }55 }
56 const zo = macho_file.getZigObject().?;
56 const off = zig_got.entryOffset(index, macho_file);57 const off = zig_got.entryOffset(index, macho_file);
57 const entry = zig_got.entries.items[index];58 const entry = zig_got.entries.items[index];
58 const value = macho_file.getSymbol(entry).getAddress(.{ .stubs = false }, macho_file);59 const value = zo.symbols.items[entry].getAddress(.{ .stubs = false }, macho_file);
5960
60 var buf: [8]u8 = undefined;61 var buf: [8]u8 = undefined;
61 std.mem.writeInt(u64, &buf, value, .little);62 std.mem.writeInt(u64, &buf, value, .little);
...@@ -63,29 +64,14 @@ pub const ZigGotSection = struct {...@@ -63,29 +64,14 @@ pub const ZigGotSection = struct {
63 }64 }
6465
65 pub fn writeAll(zig_got: ZigGotSection, macho_file: *MachO, writer: anytype) !void {66 pub fn writeAll(zig_got: ZigGotSection, macho_file: *MachO, writer: anytype) !void {
67 const zo = macho_file.getZigObject().?;
66 for (zig_got.entries.items) |entry| {68 for (zig_got.entries.items) |entry| {
67 const symbol = macho_file.getSymbol(entry);69 const symbol = zo.symbols.items[entry];
68 const value = symbol.address(.{ .stubs = false }, macho_file);70 const value = symbol.address(.{ .stubs = false }, macho_file);
69 try writer.writeInt(u64, value, .little);71 try writer.writeInt(u64, value, .little);
70 }72 }
71 }73 }
7274
73 pub fn addDyldRelocs(zig_got: ZigGotSection, macho_file: *MachO) !void {
74 const tracy = trace(@src());
75 defer tracy.end();
76 const gpa = macho_file.base.comp.gpa;
77 const seg_id = macho_file.sections.items(.segment_id)[macho_file.zig_got_sect_index.?];
78 const seg = macho_file.segments.items[seg_id];
79
80 for (0..zig_got.entries.items.len) |idx| {
81 const addr = zig_got.entryAddress(@intCast(idx), macho_file);
82 try macho_file.rebase.entries.append(gpa, .{
83 .offset = addr - seg.vmaddr,
84 .segment_id = seg_id,
85 });
86 }
87 }
88
89 const FormatCtx = struct {75 const FormatCtx = struct {
90 zig_got: ZigGotSection,76 zig_got: ZigGotSection,
91 macho_file: *MachO,77 macho_file: *MachO,
...@@ -103,22 +89,25 @@ pub const ZigGotSection = struct {...@@ -103,22 +89,25 @@ pub const ZigGotSection = struct {
103 ) !void {89 ) !void {
104 _ = options;90 _ = options;
105 _ = unused_fmt_string;91 _ = unused_fmt_string;
92 const zig_got = ctx.zig_got;
93 const macho_file = ctx.macho_file;
106 try writer.writeAll("__zig_got\n");94 try writer.writeAll("__zig_got\n");
107 for (ctx.zig_got.entries.items, 0..) |entry, index| {95 for (zig_got.entries.items, 0..) |entry, index| {
108 const symbol = ctx.macho_file.getSymbol(entry);96 const zo = macho_file.getZigObject().?;
97 const symbol = zo.symbols.items[entry];
109 try writer.print(" {d}@0x{x} => {d}@0x{x} ({s})\n", .{98 try writer.print(" {d}@0x{x} => {d}@0x{x} ({s})\n", .{
110 index,99 index,
111 ctx.zig_got.entryAddress(@intCast(index), ctx.macho_file),100 zig_got.entryAddress(@intCast(index), macho_file),
112 entry,101 entry,
113 symbol.getAddress(.{}, ctx.macho_file),102 symbol.getAddress(.{}, macho_file),
114 symbol.getName(ctx.macho_file),103 symbol.getName(macho_file),
115 });104 });
116 }105 }
117 }106 }
118};107};
119108
120pub const GotSection = struct {109pub const GotSection = struct {
121 symbols: std.ArrayListUnmanaged(Symbol.Index) = .{},110 symbols: std.ArrayListUnmanaged(MachO.Ref) = .{},
122111
123 pub const Index = u32;112 pub const Index = u32;
124113
...@@ -126,14 +115,14 @@ pub const GotSection = struct {...@@ -126,14 +115,14 @@ pub const GotSection = struct {
126 got.symbols.deinit(allocator);115 got.symbols.deinit(allocator);
127 }116 }
128117
129 pub fn addSymbol(got: *GotSection, sym_index: Symbol.Index, macho_file: *MachO) !void {118 pub fn addSymbol(got: *GotSection, ref: MachO.Ref, macho_file: *MachO) !void {
130 const gpa = macho_file.base.comp.gpa;119 const gpa = macho_file.base.comp.gpa;
131 const index = @as(Index, @intCast(got.symbols.items.len));120 const index = @as(Index, @intCast(got.symbols.items.len));
132 const entry = try got.symbols.addOne(gpa);121 const entry = try got.symbols.addOne(gpa);
133 entry.* = sym_index;122 entry.* = ref;
134 const symbol = macho_file.getSymbol(sym_index);123 const symbol = ref.getSymbol(macho_file).?;
135 symbol.flags.has_got = true;124 symbol.flags.has_got = true;
136 try symbol.addExtra(.{ .got = index }, macho_file);125 symbol.addExtra(.{ .got = index }, macho_file);
137 }126 }
138127
139 pub fn getAddress(got: GotSection, index: Index, macho_file: *MachO) u64 {128 pub fn getAddress(got: GotSection, index: Index, macho_file: *MachO) u64 {
...@@ -146,46 +135,11 @@ pub const GotSection = struct {...@@ -146,46 +135,11 @@ pub const GotSection = struct {
146 return got.symbols.items.len * @sizeOf(u64);135 return got.symbols.items.len * @sizeOf(u64);
147 }136 }
148137
149 pub fn addDyldRelocs(got: GotSection, macho_file: *MachO) !void {
150 const tracy = trace(@src());
151 defer tracy.end();
152 const gpa = macho_file.base.comp.gpa;
153 const seg_id = macho_file.sections.items(.segment_id)[macho_file.got_sect_index.?];
154 const seg = macho_file.segments.items[seg_id];
155
156 for (got.symbols.items, 0..) |sym_index, idx| {
157 const sym = macho_file.getSymbol(sym_index);
158 const addr = got.getAddress(@intCast(idx), macho_file);
159 const entry = bind.Entry{
160 .target = sym_index,
161 .offset = addr - seg.vmaddr,
162 .segment_id = seg_id,
163 .addend = 0,
164 };
165 if (sym.flags.import) {
166 try macho_file.bind.entries.append(gpa, entry);
167 if (sym.flags.weak) {
168 try macho_file.weak_bind.entries.append(gpa, entry);
169 }
170 } else {
171 try macho_file.rebase.entries.append(gpa, .{
172 .offset = addr - seg.vmaddr,
173 .segment_id = seg_id,
174 });
175 if (sym.flags.weak) {
176 try macho_file.weak_bind.entries.append(gpa, entry);
177 } else if (sym.flags.interposable) {
178 try macho_file.bind.entries.append(gpa, entry);
179 }
180 }
181 }
182 }
183
184 pub fn write(got: GotSection, macho_file: *MachO, writer: anytype) !void {138 pub fn write(got: GotSection, macho_file: *MachO, writer: anytype) !void {
185 const tracy = trace(@src());139 const tracy = trace(@src());
186 defer tracy.end();140 defer tracy.end();
187 for (got.symbols.items) |sym_index| {141 for (got.symbols.items) |ref| {
188 const sym = macho_file.getSymbol(sym_index);142 const sym = ref.getSymbol(macho_file).?;
189 const value = if (sym.flags.import) @as(u64, 0) else sym.getAddress(.{}, macho_file);143 const value = if (sym.flags.import) @as(u64, 0) else sym.getAddress(.{}, macho_file);
190 try writer.writeInt(u64, value, .little);144 try writer.writeInt(u64, value, .little);
191 }145 }
...@@ -208,12 +162,12 @@ pub const GotSection = struct {...@@ -208,12 +162,12 @@ pub const GotSection = struct {
208 ) !void {162 ) !void {
209 _ = options;163 _ = options;
210 _ = unused_fmt_string;164 _ = unused_fmt_string;
211 for (ctx.got.symbols.items, 0..) |entry, i| {165 for (ctx.got.symbols.items, 0..) |ref, i| {
212 const symbol = ctx.macho_file.getSymbol(entry);166 const symbol = ref.getSymbol(ctx.macho_file).?;
213 try writer.print(" {d}@0x{x} => {d}@0x{x} ({s})\n", .{167 try writer.print(" {d}@0x{x} => {d}@0x{x} ({s})\n", .{
214 i,168 i,
215 symbol.getGotAddress(ctx.macho_file),169 symbol.getGotAddress(ctx.macho_file),
216 entry,170 ref,
217 symbol.getAddress(.{}, ctx.macho_file),171 symbol.getAddress(.{}, ctx.macho_file),
218 symbol.getName(ctx.macho_file),172 symbol.getName(ctx.macho_file),
219 });173 });
...@@ -222,7 +176,7 @@ pub const GotSection = struct {...@@ -222,7 +176,7 @@ pub const GotSection = struct {
222};176};
223177
224pub const StubsSection = struct {178pub const StubsSection = struct {
225 symbols: std.ArrayListUnmanaged(Symbol.Index) = .{},179 symbols: std.ArrayListUnmanaged(MachO.Ref) = .{},
226180
227 pub const Index = u32;181 pub const Index = u32;
228182
...@@ -230,13 +184,13 @@ pub const StubsSection = struct {...@@ -230,13 +184,13 @@ pub const StubsSection = struct {
230 stubs.symbols.deinit(allocator);184 stubs.symbols.deinit(allocator);
231 }185 }
232186
233 pub fn addSymbol(stubs: *StubsSection, sym_index: Symbol.Index, macho_file: *MachO) !void {187 pub fn addSymbol(stubs: *StubsSection, ref: MachO.Ref, macho_file: *MachO) !void {
234 const gpa = macho_file.base.comp.gpa;188 const gpa = macho_file.base.comp.gpa;
235 const index = @as(Index, @intCast(stubs.symbols.items.len));189 const index = @as(Index, @intCast(stubs.symbols.items.len));
236 const entry = try stubs.symbols.addOne(gpa);190 const entry = try stubs.symbols.addOne(gpa);
237 entry.* = sym_index;191 entry.* = ref;
238 const symbol = macho_file.getSymbol(sym_index);192 const symbol = ref.getSymbol(macho_file).?;
239 try symbol.addExtra(.{ .stubs = index }, macho_file);193 symbol.addExtra(.{ .stubs = index }, macho_file);
240 }194 }
241195
242 pub fn getAddress(stubs: StubsSection, index: Index, macho_file: *MachO) u64 {196 pub fn getAddress(stubs: StubsSection, index: Index, macho_file: *MachO) u64 {
...@@ -256,8 +210,8 @@ pub const StubsSection = struct {...@@ -256,8 +210,8 @@ pub const StubsSection = struct {
256 const cpu_arch = macho_file.getTarget().cpu.arch;210 const cpu_arch = macho_file.getTarget().cpu.arch;
257 const laptr_sect = macho_file.sections.items(.header)[macho_file.la_symbol_ptr_sect_index.?];211 const laptr_sect = macho_file.sections.items(.header)[macho_file.la_symbol_ptr_sect_index.?];
258212
259 for (stubs.symbols.items, 0..) |sym_index, idx| {213 for (stubs.symbols.items, 0..) |ref, idx| {
260 const sym = macho_file.getSymbol(sym_index);214 const sym = ref.getSymbol(macho_file).?;
261 const source = sym.getAddress(.{ .stubs = true }, macho_file);215 const source = sym.getAddress(.{ .stubs = true }, macho_file);
262 const target = laptr_sect.addr + idx * @sizeOf(u64);216 const target = laptr_sect.addr + idx * @sizeOf(u64);
263 switch (cpu_arch) {217 switch (cpu_arch) {
...@@ -299,12 +253,12 @@ pub const StubsSection = struct {...@@ -299,12 +253,12 @@ pub const StubsSection = struct {
299 ) !void {253 ) !void {
300 _ = options;254 _ = options;
301 _ = unused_fmt_string;255 _ = unused_fmt_string;
302 for (ctx.stubs.symbols.items, 0..) |entry, i| {256 for (ctx.stubs.symbols.items, 0..) |ref, i| {
303 const symbol = ctx.macho_file.getSymbol(entry);257 const symbol = ref.getSymbol(ctx.macho_file).?;
304 try writer.print(" {d}@0x{x} => {d}@0x{x} ({s})\n", .{258 try writer.print(" {d}@0x{x} => {d}@0x{x} ({s})\n", .{
305 i,259 i,
306 symbol.getStubsAddress(ctx.macho_file),260 symbol.getStubsAddress(ctx.macho_file),
307 entry,261 ref,
308 symbol.getAddress(.{}, ctx.macho_file),262 symbol.getAddress(.{}, ctx.macho_file),
309 symbol.getName(ctx.macho_file),263 symbol.getName(ctx.macho_file),
310 });264 });
...@@ -335,8 +289,8 @@ pub const StubsHelperSection = struct {...@@ -335,8 +289,8 @@ pub const StubsHelperSection = struct {
335 _ = stubs_helper;289 _ = stubs_helper;
336 const cpu_arch = macho_file.getTarget().cpu.arch;290 const cpu_arch = macho_file.getTarget().cpu.arch;
337 var s: usize = preambleSize(cpu_arch);291 var s: usize = preambleSize(cpu_arch);
338 for (macho_file.stubs.symbols.items) |sym_index| {292 for (macho_file.stubs.symbols.items) |ref| {
339 const sym = macho_file.getSymbol(sym_index);293 const sym = ref.getSymbol(macho_file).?;
340 if (sym.flags.weak) continue;294 if (sym.flags.weak) continue;
341 s += entrySize(cpu_arch);295 s += entrySize(cpu_arch);
342 }296 }
...@@ -355,8 +309,8 @@ pub const StubsHelperSection = struct {...@@ -355,8 +309,8 @@ pub const StubsHelperSection = struct {
355 const entry_size = entrySize(cpu_arch);309 const entry_size = entrySize(cpu_arch);
356310
357 var idx: usize = 0;311 var idx: usize = 0;
358 for (macho_file.stubs.symbols.items) |sym_index| {312 for (macho_file.stubs.symbols.items) |ref| {
359 const sym = macho_file.getSymbol(sym_index);313 const sym = ref.getSymbol(macho_file).?;
360 if (sym.flags.weak) continue;314 if (sym.flags.weak) continue;
361 const offset = macho_file.lazy_bind.offsets.items[idx];315 const offset = macho_file.lazy_bind.offsets.items[idx];
362 const source: i64 = @intCast(sect.addr + preamble_size + entry_size * idx);316 const source: i64 = @intCast(sect.addr + preamble_size + entry_size * idx);
...@@ -390,14 +344,15 @@ pub const StubsHelperSection = struct {...@@ -390,14 +344,15 @@ pub const StubsHelperSection = struct {
390344
391 fn writePreamble(stubs_helper: StubsHelperSection, macho_file: *MachO, writer: anytype) !void {345 fn writePreamble(stubs_helper: StubsHelperSection, macho_file: *MachO, writer: anytype) !void {
392 _ = stubs_helper;346 _ = stubs_helper;
347 const obj = macho_file.getInternalObject().?;
393 const cpu_arch = macho_file.getTarget().cpu.arch;348 const cpu_arch = macho_file.getTarget().cpu.arch;
394 const sect = macho_file.sections.items(.header)[macho_file.stubs_helper_sect_index.?];349 const sect = macho_file.sections.items(.header)[macho_file.stubs_helper_sect_index.?];
395 const dyld_private_addr = target: {350 const dyld_private_addr = target: {
396 const sym = macho_file.getSymbol(macho_file.dyld_private_index.?);351 const sym = obj.getDyldPrivateRef(macho_file).?.getSymbol(macho_file).?;
397 break :target sym.getAddress(.{}, macho_file);352 break :target sym.getAddress(.{}, macho_file);
398 };353 };
399 const dyld_stub_binder_addr = target: {354 const dyld_stub_binder_addr = target: {
400 const sym = macho_file.getSymbol(macho_file.dyld_stub_binder_index.?);355 const sym = obj.getDyldStubBinderRef(macho_file).?.getSymbol(macho_file).?;
401 break :target sym.getGotAddress(macho_file);356 break :target sym.getGotAddress(macho_file);
402 };357 };
403 switch (cpu_arch) {358 switch (cpu_arch) {
...@@ -446,49 +401,6 @@ pub const LaSymbolPtrSection = struct {...@@ -446,49 +401,6 @@ pub const LaSymbolPtrSection = struct {
446 return macho_file.stubs.symbols.items.len * @sizeOf(u64);401 return macho_file.stubs.symbols.items.len * @sizeOf(u64);
447 }402 }
448403
449 pub fn addDyldRelocs(laptr: LaSymbolPtrSection, macho_file: *MachO) !void {
450 const tracy = trace(@src());
451 defer tracy.end();
452 _ = laptr;
453 const gpa = macho_file.base.comp.gpa;
454
455 const sect = macho_file.sections.items(.header)[macho_file.la_symbol_ptr_sect_index.?];
456 const seg_id = macho_file.sections.items(.segment_id)[macho_file.la_symbol_ptr_sect_index.?];
457 const seg = macho_file.segments.items[seg_id];
458
459 for (macho_file.stubs.symbols.items, 0..) |sym_index, idx| {
460 const sym = macho_file.getSymbol(sym_index);
461 const addr = sect.addr + idx * @sizeOf(u64);
462 const rebase_entry = Rebase.Entry{
463 .offset = addr - seg.vmaddr,
464 .segment_id = seg_id,
465 };
466 const bind_entry = bind.Entry{
467 .target = sym_index,
468 .offset = addr - seg.vmaddr,
469 .segment_id = seg_id,
470 .addend = 0,
471 };
472 if (sym.flags.import) {
473 if (sym.flags.weak) {
474 try macho_file.bind.entries.append(gpa, bind_entry);
475 try macho_file.weak_bind.entries.append(gpa, bind_entry);
476 } else {
477 try macho_file.lazy_bind.entries.append(gpa, bind_entry);
478 try macho_file.rebase.entries.append(gpa, rebase_entry);
479 }
480 } else {
481 if (sym.flags.weak) {
482 try macho_file.rebase.entries.append(gpa, rebase_entry);
483 try macho_file.weak_bind.entries.append(gpa, bind_entry);
484 } else if (sym.flags.interposable) {
485 try macho_file.lazy_bind.entries.append(gpa, bind_entry);
486 try macho_file.rebase.entries.append(gpa, rebase_entry);
487 }
488 }
489 }
490 }
491
492 pub fn write(laptr: LaSymbolPtrSection, macho_file: *MachO, writer: anytype) !void {404 pub fn write(laptr: LaSymbolPtrSection, macho_file: *MachO, writer: anytype) !void {
493 const tracy = trace(@src());405 const tracy = trace(@src());
494 defer tracy.end();406 defer tracy.end();
...@@ -496,8 +408,8 @@ pub const LaSymbolPtrSection = struct {...@@ -496,8 +408,8 @@ pub const LaSymbolPtrSection = struct {
496 const cpu_arch = macho_file.getTarget().cpu.arch;408 const cpu_arch = macho_file.getTarget().cpu.arch;
497 const sect = macho_file.sections.items(.header)[macho_file.stubs_helper_sect_index.?];409 const sect = macho_file.sections.items(.header)[macho_file.stubs_helper_sect_index.?];
498 var stub_helper_idx: u32 = 0;410 var stub_helper_idx: u32 = 0;
499 for (macho_file.stubs.symbols.items) |sym_index| {411 for (macho_file.stubs.symbols.items) |ref| {
500 const sym = macho_file.getSymbol(sym_index);412 const sym = ref.getSymbol(macho_file).?;
501 if (sym.flags.weak) {413 if (sym.flags.weak) {
502 const value = sym.getAddress(.{ .stubs = false }, macho_file);414 const value = sym.getAddress(.{ .stubs = false }, macho_file);
503 try writer.writeInt(u64, @intCast(value), .little);415 try writer.writeInt(u64, @intCast(value), .little);
...@@ -512,7 +424,7 @@ pub const LaSymbolPtrSection = struct {...@@ -512,7 +424,7 @@ pub const LaSymbolPtrSection = struct {
512};424};
513425
514pub const TlvPtrSection = struct {426pub const TlvPtrSection = struct {
515 symbols: std.ArrayListUnmanaged(Symbol.Index) = .{},427 symbols: std.ArrayListUnmanaged(MachO.Ref) = .{},
516428
517 pub const Index = u32;429 pub const Index = u32;
518430
...@@ -520,13 +432,13 @@ pub const TlvPtrSection = struct {...@@ -520,13 +432,13 @@ pub const TlvPtrSection = struct {
520 tlv.symbols.deinit(allocator);432 tlv.symbols.deinit(allocator);
521 }433 }
522434
523 pub fn addSymbol(tlv: *TlvPtrSection, sym_index: Symbol.Index, macho_file: *MachO) !void {435 pub fn addSymbol(tlv: *TlvPtrSection, ref: MachO.Ref, macho_file: *MachO) !void {
524 const gpa = macho_file.base.comp.gpa;436 const gpa = macho_file.base.comp.gpa;
525 const index = @as(Index, @intCast(tlv.symbols.items.len));437 const index = @as(Index, @intCast(tlv.symbols.items.len));
526 const entry = try tlv.symbols.addOne(gpa);438 const entry = try tlv.symbols.addOne(gpa);
527 entry.* = sym_index;439 entry.* = ref;
528 const symbol = macho_file.getSymbol(sym_index);440 const symbol = ref.getSymbol(macho_file).?;
529 try symbol.addExtra(.{ .tlv_ptr = index }, macho_file);441 symbol.addExtra(.{ .tlv_ptr = index }, macho_file);
530 }442 }
531443
532 pub fn getAddress(tlv: TlvPtrSection, index: Index, macho_file: *MachO) u64 {444 pub fn getAddress(tlv: TlvPtrSection, index: Index, macho_file: *MachO) u64 {
...@@ -539,47 +451,12 @@ pub const TlvPtrSection = struct {...@@ -539,47 +451,12 @@ pub const TlvPtrSection = struct {
539 return tlv.symbols.items.len * @sizeOf(u64);451 return tlv.symbols.items.len * @sizeOf(u64);
540 }452 }
541453
542 pub fn addDyldRelocs(tlv: TlvPtrSection, macho_file: *MachO) !void {
543 const tracy = trace(@src());
544 defer tracy.end();
545 const gpa = macho_file.base.comp.gpa;
546 const seg_id = macho_file.sections.items(.segment_id)[macho_file.tlv_ptr_sect_index.?];
547 const seg = macho_file.segments.items[seg_id];
548
549 for (tlv.symbols.items, 0..) |sym_index, idx| {
550 const sym = macho_file.getSymbol(sym_index);
551 const addr = tlv.getAddress(@intCast(idx), macho_file);
552 const entry = bind.Entry{
553 .target = sym_index,
554 .offset = addr - seg.vmaddr,
555 .segment_id = seg_id,
556 .addend = 0,
557 };
558 if (sym.flags.import) {
559 try macho_file.bind.entries.append(gpa, entry);
560 if (sym.flags.weak) {
561 try macho_file.weak_bind.entries.append(gpa, entry);
562 }
563 } else {
564 try macho_file.rebase.entries.append(gpa, .{
565 .offset = addr - seg.vmaddr,
566 .segment_id = seg_id,
567 });
568 if (sym.flags.weak) {
569 try macho_file.weak_bind.entries.append(gpa, entry);
570 } else if (sym.flags.interposable) {
571 try macho_file.bind.entries.append(gpa, entry);
572 }
573 }
574 }
575 }
576
577 pub fn write(tlv: TlvPtrSection, macho_file: *MachO, writer: anytype) !void {454 pub fn write(tlv: TlvPtrSection, macho_file: *MachO, writer: anytype) !void {
578 const tracy = trace(@src());455 const tracy = trace(@src());
579 defer tracy.end();456 defer tracy.end();
580457
581 for (tlv.symbols.items) |sym_index| {458 for (tlv.symbols.items) |ref| {
582 const sym = macho_file.getSymbol(sym_index);459 const sym = ref.getSymbol(macho_file).?;
583 if (sym.flags.import) {460 if (sym.flags.import) {
584 try writer.writeInt(u64, 0, .little);461 try writer.writeInt(u64, 0, .little);
585 } else {462 } else {
...@@ -605,12 +482,12 @@ pub const TlvPtrSection = struct {...@@ -605,12 +482,12 @@ pub const TlvPtrSection = struct {
605 ) !void {482 ) !void {
606 _ = options;483 _ = options;
607 _ = unused_fmt_string;484 _ = unused_fmt_string;
608 for (ctx.tlv.symbols.items, 0..) |entry, i| {485 for (ctx.tlv.symbols.items, 0..) |ref, i| {
609 const symbol = ctx.macho_file.getSymbol(entry);486 const symbol = ref.getSymbol(ctx.macho_file).?;
610 try writer.print(" {d}@0x{x} => {d}@0x{x} ({s})\n", .{487 try writer.print(" {d}@0x{x} => {d}@0x{x} ({s})\n", .{
611 i,488 i,
612 symbol.getTlvPtrAddress(ctx.macho_file),489 symbol.getTlvPtrAddress(ctx.macho_file),
613 entry,490 ref,
614 symbol.getAddress(.{}, ctx.macho_file),491 symbol.getAddress(.{}, ctx.macho_file),
615 symbol.getName(ctx.macho_file),492 symbol.getName(ctx.macho_file),
616 });493 });
...@@ -619,7 +496,7 @@ pub const TlvPtrSection = struct {...@@ -619,7 +496,7 @@ pub const TlvPtrSection = struct {
619};496};
620497
621pub const ObjcStubsSection = struct {498pub const ObjcStubsSection = struct {
622 symbols: std.ArrayListUnmanaged(Symbol.Index) = .{},499 symbols: std.ArrayListUnmanaged(MachO.Ref) = .{},
623500
624 pub fn deinit(objc: *ObjcStubsSection, allocator: Allocator) void {501 pub fn deinit(objc: *ObjcStubsSection, allocator: Allocator) void {
625 objc.symbols.deinit(allocator);502 objc.symbols.deinit(allocator);
...@@ -633,13 +510,13 @@ pub const ObjcStubsSection = struct {...@@ -633,13 +510,13 @@ pub const ObjcStubsSection = struct {
633 };510 };
634 }511 }
635512
636 pub fn addSymbol(objc: *ObjcStubsSection, sym_index: Symbol.Index, macho_file: *MachO) !void {513 pub fn addSymbol(objc: *ObjcStubsSection, ref: MachO.Ref, macho_file: *MachO) !void {
637 const gpa = macho_file.base.comp.gpa;514 const gpa = macho_file.base.comp.gpa;
638 const index = @as(Index, @intCast(objc.symbols.items.len));515 const index = @as(Index, @intCast(objc.symbols.items.len));
639 const entry = try objc.symbols.addOne(gpa);516 const entry = try objc.symbols.addOne(gpa);
640 entry.* = sym_index;517 entry.* = ref;
641 const symbol = macho_file.getSymbol(sym_index);518 const symbol = ref.getSymbol(macho_file).?;
642 try symbol.addExtra(.{ .objc_stubs = index }, macho_file);519 symbol.addExtra(.{ .objc_stubs = index }, macho_file);
643 }520 }
644521
645 pub fn getAddress(objc: ObjcStubsSection, index: Index, macho_file: *MachO) u64 {522 pub fn getAddress(objc: ObjcStubsSection, index: Index, macho_file: *MachO) u64 {
...@@ -656,8 +533,10 @@ pub const ObjcStubsSection = struct {...@@ -656,8 +533,10 @@ pub const ObjcStubsSection = struct {
656 const tracy = trace(@src());533 const tracy = trace(@src());
657 defer tracy.end();534 defer tracy.end();
658535
659 for (objc.symbols.items, 0..) |sym_index, idx| {536 const obj = macho_file.getInternalObject().?;
660 const sym = macho_file.getSymbol(sym_index);537
538 for (objc.symbols.items, 0..) |ref, idx| {
539 const sym = ref.getSymbol(macho_file).?;
661 const addr = objc.getAddress(@intCast(idx), macho_file);540 const addr = objc.getAddress(@intCast(idx), macho_file);
662 switch (macho_file.getTarget().cpu.arch) {541 switch (macho_file.getTarget().cpu.arch) {
663 .x86_64 => {542 .x86_64 => {
...@@ -669,7 +548,7 @@ pub const ObjcStubsSection = struct {...@@ -669,7 +548,7 @@ pub const ObjcStubsSection = struct {
669 }548 }
670 try writer.writeAll(&.{ 0xff, 0x25 });549 try writer.writeAll(&.{ 0xff, 0x25 });
671 {550 {
672 const target_sym = macho_file.getSymbol(macho_file.objc_msg_send_index.?);551 const target_sym = obj.getObjcMsgSendRef(macho_file).?.getSymbol(macho_file).?;
673 const target = target_sym.getGotAddress(macho_file);552 const target = target_sym.getGotAddress(macho_file);
674 const source = addr + 7;553 const source = addr + 7;
675 try writer.writeInt(i32, @intCast(target - source - 2 - 4), .little);554 try writer.writeInt(i32, @intCast(target - source - 2 - 4), .little);
...@@ -689,7 +568,7 @@ pub const ObjcStubsSection = struct {...@@ -689,7 +568,7 @@ pub const ObjcStubsSection = struct {
689 );568 );
690 }569 }
691 {570 {
692 const target_sym = macho_file.getSymbol(macho_file.objc_msg_send_index.?);571 const target_sym = obj.getObjcMsgSendRef(macho_file).?.getSymbol(macho_file).?;
693 const target = target_sym.getGotAddress(macho_file);572 const target = target_sym.getGotAddress(macho_file);
694 const source = addr + 2 * @sizeOf(u32);573 const source = addr + 2 * @sizeOf(u32);
695 const pages = try aarch64.calcNumberOfPages(@intCast(source), @intCast(target));574 const pages = try aarch64.calcNumberOfPages(@intCast(source), @intCast(target));
...@@ -728,12 +607,12 @@ pub const ObjcStubsSection = struct {...@@ -728,12 +607,12 @@ pub const ObjcStubsSection = struct {
728 ) !void {607 ) !void {
729 _ = options;608 _ = options;
730 _ = unused_fmt_string;609 _ = unused_fmt_string;
731 for (ctx.objc.symbols.items, 0..) |entry, i| {610 for (ctx.objc.symbols.items, 0..) |ref, i| {
732 const symbol = ctx.macho_file.getSymbol(entry);611 const symbol = ref.getSymbol(ctx.macho_file).?;
733 try writer.print(" {d}@0x{x} => {d}@0x{x} ({s})\n", .{612 try writer.print(" {d}@0x{x} => {d}@0x{x} ({s})\n", .{
734 i,613 i,
735 symbol.getObjcStubsAddress(ctx.macho_file),614 symbol.getObjcStubsAddress(ctx.macho_file),
736 entry,615 ref,
737 symbol.getAddress(.{}, ctx.macho_file),616 symbol.getAddress(.{}, ctx.macho_file),
738 symbol.getName(ctx.macho_file),617 symbol.getName(ctx.macho_file),
739 });618 });
...@@ -749,44 +628,112 @@ pub const Indsymtab = struct {...@@ -749,44 +628,112 @@ pub const Indsymtab = struct {
749 return @intCast(macho_file.stubs.symbols.items.len * 2 + macho_file.got.symbols.items.len);628 return @intCast(macho_file.stubs.symbols.items.len * 2 + macho_file.got.symbols.items.len);
750 }629 }
751630
631 pub fn updateSize(ind: *Indsymtab, macho_file: *MachO) !void {
632 macho_file.dysymtab_cmd.nindirectsyms = ind.nsyms(macho_file);
633 }
634
752 pub fn write(ind: Indsymtab, macho_file: *MachO, writer: anytype) !void {635 pub fn write(ind: Indsymtab, macho_file: *MachO, writer: anytype) !void {
753 const tracy = trace(@src());636 const tracy = trace(@src());
754 defer tracy.end();637 defer tracy.end();
755638
756 _ = ind;639 _ = ind;
757640
758 for (macho_file.stubs.symbols.items) |sym_index| {641 for (macho_file.stubs.symbols.items) |ref| {
759 const sym = macho_file.getSymbol(sym_index);642 const sym = ref.getSymbol(macho_file).?;
760 try writer.writeInt(u32, sym.getOutputSymtabIndex(macho_file).?, .little);643 try writer.writeInt(u32, sym.getOutputSymtabIndex(macho_file).?, .little);
761 }644 }
762645
763 for (macho_file.got.symbols.items) |sym_index| {646 for (macho_file.got.symbols.items) |ref| {
764 const sym = macho_file.getSymbol(sym_index);647 const sym = ref.getSymbol(macho_file).?;
765 try writer.writeInt(u32, sym.getOutputSymtabIndex(macho_file).?, .little);648 try writer.writeInt(u32, sym.getOutputSymtabIndex(macho_file).?, .little);
766 }649 }
767650
768 for (macho_file.stubs.symbols.items) |sym_index| {651 for (macho_file.stubs.symbols.items) |ref| {
769 const sym = macho_file.getSymbol(sym_index);652 const sym = ref.getSymbol(macho_file).?;
770 try writer.writeInt(u32, sym.getOutputSymtabIndex(macho_file).?, .little);653 try writer.writeInt(u32, sym.getOutputSymtabIndex(macho_file).?, .little);
771 }654 }
772 }655 }
773};656};
774657
775pub const RebaseSection = Rebase;658pub const DataInCode = struct {
776pub const BindSection = bind.Bind;659 entries: std.ArrayListUnmanaged(Entry) = .{},
777pub const WeakBindSection = bind.WeakBind;660
778pub const LazyBindSection = bind.LazyBind;661 pub fn deinit(dice: *DataInCode, allocator: Allocator) void {
779pub const ExportTrieSection = Trie;662 dice.entries.deinit(allocator);
663 }
664
665 pub fn size(dice: DataInCode) usize {
666 return dice.entries.items.len * @sizeOf(macho.data_in_code_entry);
667 }
668
669 pub fn updateSize(dice: *DataInCode, macho_file: *MachO) !void {
670 const gpa = macho_file.base.comp.gpa;
671
672 for (macho_file.objects.items) |index| {
673 const object = macho_file.getFile(index).?.object;
674 const dices = object.getDataInCode();
675
676 try dice.entries.ensureUnusedCapacity(gpa, dices.len);
677
678 var next_dice: usize = 0;
679 for (object.getAtoms()) |atom_index| {
680 if (next_dice >= dices.len) break;
681 const atom = object.getAtom(atom_index) orelse continue;
682 const start_off = atom.getInputAddress(macho_file);
683 const end_off = start_off + atom.size;
684 const start_dice = next_dice;
685
686 if (end_off < dices[next_dice].offset) continue;
687
688 while (next_dice < dices.len and
689 dices[next_dice].offset < end_off) : (next_dice += 1)
690 {}
691
692 if (atom.flags.alive) for (dices[start_dice..next_dice]) |d| {
693 dice.entries.appendAssumeCapacity(.{
694 .atom_ref = .{ .index = atom_index, .file = index },
695 .offset = @intCast(d.offset - start_off),
696 .length = d.length,
697 .kind = d.kind,
698 });
699 };
700 }
701 }
702
703 macho_file.data_in_code_cmd.datasize = math.cast(u32, dice.size()) orelse return error.Overflow;
704 }
705
706 pub fn write(dice: DataInCode, macho_file: *MachO, writer: anytype) !void {
707 const base_address = if (!macho_file.base.isRelocatable())
708 macho_file.getTextSegment().vmaddr
709 else
710 0;
711 for (dice.entries.items) |entry| {
712 const atom_address = entry.atom_ref.getAtom(macho_file).?.getAddress(macho_file);
713 const offset = atom_address + entry.offset - base_address;
714 try writer.writeStruct(macho.data_in_code_entry{
715 .offset = @intCast(offset),
716 .length = entry.length,
717 .kind = entry.kind,
718 });
719 }
720 }
721
722 const Entry = struct {
723 atom_ref: MachO.Ref,
724 offset: u32,
725 length: u16,
726 kind: u16,
727 };
728};
780729
781const aarch64 = @import("../aarch64.zig");730const aarch64 = @import("../aarch64.zig");
782const assert = std.debug.assert;731const assert = std.debug.assert;
783const bind = @import("dyld_info/bind.zig");732const macho = std.macho;
784const math = std.math;733const math = std.math;
785const std = @import("std");734const std = @import("std");
786const trace = @import("../../tracy.zig").trace;735const trace = @import("../../tracy.zig").trace;
787736
788const Allocator = std.mem.Allocator;737const Allocator = std.mem.Allocator;
789const MachO = @import("../MachO.zig");738const MachO = @import("../MachO.zig");
790const Rebase = @import("dyld_info/Rebase.zig");
791const Symbol = @import("Symbol.zig");739const Symbol = @import("Symbol.zig");
792const Trie = @import("dyld_info/Trie.zig");
src/link/MachO/thunks.zig+40-32
...@@ -5,55 +5,45 @@ pub fn createThunks(sect_id: u8, macho_file: *MachO) !void {...@@ -5,55 +5,45 @@ pub fn createThunks(sect_id: u8, macho_file: *MachO) !void {
5 const gpa = macho_file.base.comp.gpa;5 const gpa = macho_file.base.comp.gpa;
6 const slice = macho_file.sections.slice();6 const slice = macho_file.sections.slice();
7 const header = &slice.items(.header)[sect_id];7 const header = &slice.items(.header)[sect_id];
8 const thnks = &slice.items(.thunks)[sect_id];
8 const atoms = slice.items(.atoms)[sect_id].items;9 const atoms = slice.items(.atoms)[sect_id].items;
9 assert(atoms.len > 0);10 assert(atoms.len > 0);
1011
11 for (atoms) |atom_index| {12 for (atoms) |ref| {
12 macho_file.getAtom(atom_index).?.value = @bitCast(@as(i64, -1));13 ref.getAtom(macho_file).?.value = @bitCast(@as(i64, -1));
13 }14 }
1415
15 var i: usize = 0;16 var i: usize = 0;
16 while (i < atoms.len) {17 while (i < atoms.len) {
17 const start = i;18 const start = i;
18 const start_atom = macho_file.getAtom(atoms[start]).?;19 const start_atom = atoms[start].getAtom(macho_file).?;
19 assert(start_atom.flags.alive);20 assert(start_atom.flags.alive);
20 start_atom.value = try advance(header, start_atom.size, start_atom.alignment);21 start_atom.value = advance(header, start_atom.size, start_atom.alignment);
21 i += 1;22 i += 1;
2223
23 while (i < atoms.len and24 while (i < atoms.len and
24 header.size - start_atom.value < max_allowed_distance) : (i += 1)25 header.size - start_atom.value < max_allowed_distance) : (i += 1)
25 {26 {
26 const atom_index = atoms[i];27 const atom = atoms[i].getAtom(macho_file).?;
27 const atom = macho_file.getAtom(atom_index).?;
28 assert(atom.flags.alive);28 assert(atom.flags.alive);
29 atom.value = try advance(header, atom.size, atom.alignment);29 atom.value = advance(header, atom.size, atom.alignment);
30 }30 }
3131
32 // Insert a thunk at the group end32 // Insert a thunk at the group end
33 const thunk_index = try macho_file.addThunk();33 const thunk_index = try macho_file.addThunk();
34 const thunk = macho_file.getThunk(thunk_index);34 const thunk = macho_file.getThunk(thunk_index);
35 thunk.out_n_sect = sect_id;35 thunk.out_n_sect = sect_id;
36 try thnks.append(gpa, thunk_index);
3637
37 // Scan relocs in the group and create trampolines for any unreachable callsite38 // Scan relocs in the group and create trampolines for any unreachable callsite
38 for (atoms[start..i]) |atom_index| {39 try scanRelocs(thunk_index, gpa, atoms[start..i], macho_file);
39 const atom = macho_file.getAtom(atom_index).?;40 thunk.value = advance(header, thunk.size(), .@"4");
40 log.debug("atom({d}) {s}", .{ atom_index, atom.getName(macho_file) });
41 for (atom.getRelocs(macho_file)) |rel| {
42 if (rel.type != .branch) continue;
43 if (isReachable(atom, rel, macho_file)) continue;
44 try thunk.symbols.put(gpa, rel.target, {});
45 }
46 try atom.addExtra(.{ .thunk = thunk_index }, macho_file);
47 atom.flags.thunk = true;
48 }
49
50 thunk.value = try advance(header, thunk.size(), .@"4");
5141
52 log.debug("thunk({d}) : {}", .{ thunk_index, thunk.fmt(macho_file) });42 log.debug("thunk({d}) : {}", .{ thunk_index, thunk.fmt(macho_file) });
53 }43 }
54}44}
5545
56fn advance(sect: *macho.section_64, size: u64, alignment: Atom.Alignment) !u64 {46fn advance(sect: *macho.section_64, size: u64, alignment: Atom.Alignment) u64 {
57 const offset = alignment.forward(sect.size);47 const offset = alignment.forward(sect.size);
58 const padding = offset - sect.size;48 const padding = offset - sect.size;
59 sect.size += padding + size;49 sect.size += padding + size;
...@@ -61,14 +51,32 @@ fn advance(sect: *macho.section_64, size: u64, alignment: Atom.Alignment) !u64 {...@@ -61,14 +51,32 @@ fn advance(sect: *macho.section_64, size: u64, alignment: Atom.Alignment) !u64 {
61 return offset;51 return offset;
62}52}
6353
54fn scanRelocs(thunk_index: Thunk.Index, gpa: Allocator, atoms: []const MachO.Ref, macho_file: *MachO) !void {
55 const tracy = trace(@src());
56 defer tracy.end();
57
58 const thunk = macho_file.getThunk(thunk_index);
59
60 for (atoms) |ref| {
61 const atom = ref.getAtom(macho_file).?;
62 log.debug("atom({d}) {s}", .{ atom.atom_index, atom.getName(macho_file) });
63 for (atom.getRelocs(macho_file)) |rel| {
64 if (rel.type != .branch) continue;
65 if (isReachable(atom, rel, macho_file)) continue;
66 try thunk.symbols.put(gpa, rel.getTargetSymbolRef(atom.*, macho_file), {});
67 }
68 atom.addExtra(.{ .thunk = thunk_index }, macho_file);
69 }
70}
71
64fn isReachable(atom: *const Atom, rel: Relocation, macho_file: *MachO) bool {72fn isReachable(atom: *const Atom, rel: Relocation, macho_file: *MachO) bool {
65 const target = rel.getTargetSymbol(macho_file);73 const target = rel.getTargetSymbol(atom.*, macho_file);
66 if (target.flags.stubs or target.flags.objc_stubs) return false;74 if (target.flags.stubs or target.flags.objc_stubs) return false;
67 if (atom.out_n_sect != target.out_n_sect) return false;75 if (atom.out_n_sect != target.getOutputSectionIndex(macho_file)) return false;
68 const target_atom = target.getAtom(macho_file).?;76 const target_atom = target.getAtom(macho_file).?;
69 if (target_atom.value == @as(u64, @bitCast(@as(i64, -1)))) return false;77 if (target_atom.value == @as(u64, @bitCast(@as(i64, -1)))) return false;
70 const saddr = @as(i64, @intCast(atom.getAddress(macho_file))) + @as(i64, @intCast(rel.offset - atom.off));78 const saddr = @as(i64, @intCast(atom.getAddress(macho_file))) + @as(i64, @intCast(rel.offset - atom.off));
71 const taddr: i64 = @intCast(rel.getTargetAddress(macho_file));79 const taddr: i64 = @intCast(rel.getTargetAddress(atom.*, macho_file));
72 _ = math.cast(i28, taddr + rel.addend - saddr) orelse return false;80 _ = math.cast(i28, taddr + rel.addend - saddr) orelse return false;
73 return true;81 return true;
74}82}
...@@ -76,7 +84,7 @@ fn isReachable(atom: *const Atom, rel: Relocation, macho_file: *MachO) bool {...@@ -76,7 +84,7 @@ fn isReachable(atom: *const Atom, rel: Relocation, macho_file: *MachO) bool {
76pub const Thunk = struct {84pub const Thunk = struct {
77 value: u64 = 0,85 value: u64 = 0,
78 out_n_sect: u8 = 0,86 out_n_sect: u8 = 0,
79 symbols: std.AutoArrayHashMapUnmanaged(Symbol.Index, void) = .{},87 symbols: std.AutoArrayHashMapUnmanaged(MachO.Ref, void) = .{},
8088
81 pub fn deinit(thunk: *Thunk, allocator: Allocator) void {89 pub fn deinit(thunk: *Thunk, allocator: Allocator) void {
82 thunk.symbols.deinit(allocator);90 thunk.symbols.deinit(allocator);
...@@ -91,13 +99,13 @@ pub const Thunk = struct {...@@ -91,13 +99,13 @@ pub const Thunk = struct {
91 return header.addr + thunk.value;99 return header.addr + thunk.value;
92 }100 }
93101
94 pub fn getTargetAddress(thunk: Thunk, sym_index: Symbol.Index, macho_file: *MachO) u64 {102 pub fn getTargetAddress(thunk: Thunk, ref: MachO.Ref, macho_file: *MachO) u64 {
95 return thunk.getAddress(macho_file) + thunk.symbols.getIndex(sym_index).? * trampoline_size;103 return thunk.getAddress(macho_file) + thunk.symbols.getIndex(ref).? * trampoline_size;
96 }104 }
97105
98 pub fn write(thunk: Thunk, macho_file: *MachO, writer: anytype) !void {106 pub fn write(thunk: Thunk, macho_file: *MachO, writer: anytype) !void {
99 for (thunk.symbols.keys(), 0..) |sym_index, i| {107 for (thunk.symbols.keys(), 0..) |ref, i| {
100 const sym = macho_file.getSymbol(sym_index);108 const sym = ref.getSymbol(macho_file).?;
101 const saddr = thunk.getAddress(macho_file) + i * trampoline_size;109 const saddr = thunk.getAddress(macho_file) + i * trampoline_size;
102 const taddr = sym.getAddress(.{}, macho_file);110 const taddr = sym.getAddress(.{}, macho_file);
103 const pages = try aarch64.calcNumberOfPages(@intCast(saddr), @intCast(taddr));111 const pages = try aarch64.calcNumberOfPages(@intCast(saddr), @intCast(taddr));
...@@ -144,9 +152,9 @@ pub const Thunk = struct {...@@ -144,9 +152,9 @@ pub const Thunk = struct {
144 const thunk = ctx.thunk;152 const thunk = ctx.thunk;
145 const macho_file = ctx.macho_file;153 const macho_file = ctx.macho_file;
146 try writer.print("@{x} : size({x})\n", .{ thunk.value, thunk.size() });154 try writer.print("@{x} : size({x})\n", .{ thunk.value, thunk.size() });
147 for (thunk.symbols.keys()) |index| {155 for (thunk.symbols.keys()) |ref| {
148 const sym = macho_file.getSymbol(index);156 const sym = ref.getSymbol(macho_file).?;
149 try writer.print(" %{d} : {s} : @{x}\n", .{ index, sym.getName(macho_file), sym.value });157 try writer.print(" {} : {s} : @{x}\n", .{ ref, sym.getName(macho_file), sym.value });
150 }158 }
151 }159 }
152160
test/link/link.zig+2-1
...@@ -74,8 +74,9 @@ fn addCompileStep(...@@ -74,8 +74,9 @@ fn addCompileStep(
74 .target = base.target,74 .target = base.target,
75 .optimize = base.optimize,75 .optimize = base.optimize,
76 .root_source_file = rsf: {76 .root_source_file = rsf: {
77 const name = b.fmt("{s}.zig", .{overlay.name});
77 const bytes = overlay.zig_source_bytes orelse break :rsf null;78 const bytes = overlay.zig_source_bytes orelse break :rsf null;
78 break :rsf b.addWriteFiles().add("a.zig", bytes);79 break :rsf b.addWriteFiles().add(name, bytes);
79 },80 },
80 .pic = overlay.pic,81 .pic = overlay.pic,
81 .strip = if (base.strip) |s| s else overlay.strip,82 .strip = if (base.strip) |s| s else overlay.strip,
test/link/macho.zig+99-1
...@@ -25,9 +25,12 @@ pub fn testAll(b: *Build, build_opts: BuildOptions) *Step {...@@ -25,9 +25,12 @@ pub fn testAll(b: *Build, build_opts: BuildOptions) *Step {
25 macho_step.dependOn(testLinkingStaticLib(b, .{ .use_llvm = false, .target = x86_64_target }));25 macho_step.dependOn(testLinkingStaticLib(b, .{ .use_llvm = false, .target = x86_64_target }));
26 macho_step.dependOn(testReexportsZig(b, .{ .use_llvm = false, .target = x86_64_target }));26 macho_step.dependOn(testReexportsZig(b, .{ .use_llvm = false, .target = x86_64_target }));
27 macho_step.dependOn(testRelocatableZig(b, .{ .use_llvm = false, .target = x86_64_target }));27 macho_step.dependOn(testRelocatableZig(b, .{ .use_llvm = false, .target = x86_64_target }));
28 macho_step.dependOn(testTlsZig(b, .{ .use_llvm = false, .target = x86_64_target }));
29 macho_step.dependOn(testUnresolvedError(b, .{ .use_llvm = false, .target = x86_64_target }));
2830
29 // Exercise linker with LLVM backend31 // Exercise linker with LLVM backend
30 macho_step.dependOn(testDeadStrip(b, .{ .target = default_target }));32 macho_step.dependOn(testDeadStrip(b, .{ .target = default_target }));
33 macho_step.dependOn(testDuplicateDefinitions(b, .{ .target = default_target }));
31 macho_step.dependOn(testEmptyObject(b, .{ .target = default_target }));34 macho_step.dependOn(testEmptyObject(b, .{ .target = default_target }));
32 macho_step.dependOn(testEmptyZig(b, .{ .target = default_target }));35 macho_step.dependOn(testEmptyZig(b, .{ .target = default_target }));
33 macho_step.dependOn(testEntryPoint(b, .{ .target = default_target }));36 macho_step.dependOn(testEntryPoint(b, .{ .target = default_target }));
...@@ -56,7 +59,9 @@ pub fn testAll(b: *Build, build_opts: BuildOptions) *Step {...@@ -56,7 +59,9 @@ pub fn testAll(b: *Build, build_opts: BuildOptions) *Step {
56 macho_step.dependOn(testTentative(b, .{ .target = default_target }));59 macho_step.dependOn(testTentative(b, .{ .target = default_target }));
57 macho_step.dependOn(testThunks(b, .{ .target = aarch64_target }));60 macho_step.dependOn(testThunks(b, .{ .target = aarch64_target }));
58 macho_step.dependOn(testTlsLargeTbss(b, .{ .target = default_target }));61 macho_step.dependOn(testTlsLargeTbss(b, .{ .target = default_target }));
62 macho_step.dependOn(testTlsZig(b, .{ .target = default_target }));
59 macho_step.dependOn(testUndefinedFlag(b, .{ .target = default_target }));63 macho_step.dependOn(testUndefinedFlag(b, .{ .target = default_target }));
64 macho_step.dependOn(testUnresolvedError(b, .{ .target = default_target }));
60 macho_step.dependOn(testUnwindInfo(b, .{ .target = default_target }));65 macho_step.dependOn(testUnwindInfo(b, .{ .target = default_target }));
61 macho_step.dependOn(testUnwindInfoNoSubsectionsX64(b, .{ .target = x86_64_target }));66 macho_step.dependOn(testUnwindInfoNoSubsectionsX64(b, .{ .target = x86_64_target }));
62 macho_step.dependOn(testUnwindInfoNoSubsectionsArm64(b, .{ .target = aarch64_target }));67 macho_step.dependOn(testUnwindInfoNoSubsectionsArm64(b, .{ .target = aarch64_target }));
...@@ -178,6 +183,37 @@ fn testDeadStrip(b: *Build, opts: Options) *Step {...@@ -178,6 +183,37 @@ fn testDeadStrip(b: *Build, opts: Options) *Step {
178 return test_step;183 return test_step;
179}184}
180185
186fn testDuplicateDefinitions(b: *Build, opts: Options) *Step {
187 const test_step = addTestStep(b, "duplicate-definitions", opts);
188
189 const obj = addObject(b, opts, .{ .name = "a", .zig_source_bytes =
190 \\var x: usize = 1;
191 \\export fn strong() void { x += 1; }
192 \\export fn weak() void { x += 1; }
193 });
194
195 const exe = addExecutable(b, opts, .{ .name = "main", .zig_source_bytes =
196 \\var x: usize = 1;
197 \\export fn strong() void { x += 1; }
198 \\comptime { @export(weakImpl, .{ .name = "weak", .linkage = .weak }); }
199 \\fn weakImpl() callconv(.C) void { x += 1; }
200 \\extern fn weak() void;
201 \\pub fn main() void {
202 \\ weak();
203 \\ strong();
204 \\}
205 });
206 exe.addObject(obj);
207
208 expectLinkErrors(exe, test_step, .{ .exact = &.{
209 "error: duplicate symbol definition: _strong",
210 "note: defined by /?/a.o",
211 "note: defined by /?/main.o",
212 } });
213
214 return test_step;
215}
216
181fn testDeadStripDylibs(b: *Build, opts: Options) *Step {217fn testDeadStripDylibs(b: *Build, opts: Options) *Step {
182 const test_step = addTestStep(b, "dead-strip-dylibs", opts);218 const test_step = addTestStep(b, "dead-strip-dylibs", opts);
183219
...@@ -912,7 +948,7 @@ fn testLinksection(b: *Build, opts: Options) *Step {...@@ -912,7 +948,7 @@ fn testLinksection(b: *Build, opts: Options) *Step {
912948
913 if (opts.optimize == .Debug) {949 if (opts.optimize == .Debug) {
914 check.checkInSymtab();950 check.checkInSymtab();
915 check.checkContains("(__TEXT,__TestGenFnA) _a.testGenericFn__anon_");951 check.checkContains("(__TEXT,__TestGenFnA) _main.testGenericFn__anon_");
916 }952 }
917953
918 test_step.dependOn(&check.step);954 test_step.dependOn(&check.step);
...@@ -2274,6 +2310,32 @@ fn testTlsLargeTbss(b: *Build, opts: Options) *Step {...@@ -2274,6 +2310,32 @@ fn testTlsLargeTbss(b: *Build, opts: Options) *Step {
2274 return test_step;2310 return test_step;
2275}2311}
22762312
2313fn testTlsZig(b: *Build, opts: Options) *Step {
2314 const test_step = addTestStep(b, "tls-zig", opts);
2315
2316 const exe = addExecutable(b, opts, .{ .name = "main", .zig_source_bytes =
2317 \\const std = @import("std");
2318 \\threadlocal var x: i32 = 0;
2319 \\threadlocal var y: i32 = -1;
2320 \\pub fn main() void {
2321 \\ std.io.getStdOut().writer().print("{d} {d}\n", .{x, y}) catch unreachable;
2322 \\ x -= 1;
2323 \\ y += 1;
2324 \\ std.io.getStdOut().writer().print("{d} {d}\n", .{x, y}) catch unreachable;
2325 \\}
2326 });
2327
2328 const run = addRunArtifact(exe);
2329 run.expectStdOutEqual(
2330 \\0 -1
2331 \\-1 0
2332 \\
2333 );
2334 test_step.dependOn(&run.step);
2335
2336 return test_step;
2337}
2338
2277fn testTwoLevelNamespace(b: *Build, opts: Options) *Step {2339fn testTwoLevelNamespace(b: *Build, opts: Options) *Step {
2278 const test_step = addTestStep(b, "two-level-namespace", opts);2340 const test_step = addTestStep(b, "two-level-namespace", opts);
22792341
...@@ -2471,6 +2533,42 @@ fn testUndefinedFlag(b: *Build, opts: Options) *Step {...@@ -2471,6 +2533,42 @@ fn testUndefinedFlag(b: *Build, opts: Options) *Step {
2471 return test_step;2533 return test_step;
2472}2534}
24732535
2536fn testUnresolvedError(b: *Build, opts: Options) *Step {
2537 const test_step = addTestStep(b, "unresolved-error", opts);
2538
2539 const obj = addObject(b, opts, .{ .name = "a", .zig_source_bytes =
2540 \\extern fn foo() i32;
2541 \\export fn bar() i32 { return foo() + 1; }
2542 });
2543
2544 const exe = addExecutable(b, opts, .{ .name = "main", .zig_source_bytes =
2545 \\const std = @import("std");
2546 \\extern fn foo() i32;
2547 \\extern fn bar() i32;
2548 \\pub fn main() void {
2549 \\ std.debug.print("foo() + bar() = {d}", .{foo() + bar()});
2550 \\}
2551 });
2552 exe.addObject(obj);
2553
2554 // TODO order should match across backends if possible
2555 if (opts.use_llvm) {
2556 expectLinkErrors(exe, test_step, .{ .exact = &.{
2557 "error: undefined symbol: _foo",
2558 "note: referenced by /?/a.o:_bar",
2559 "note: referenced by /?/main.o:_main.main",
2560 } });
2561 } else {
2562 expectLinkErrors(exe, test_step, .{ .exact = &.{
2563 "error: undefined symbol: _foo",
2564 "note: referenced by /?/main.o:_main.main",
2565 "note: referenced by /?/a.o:__TEXT$__text_zig",
2566 } });
2567 }
2568
2569 return test_step;
2570}
2571
2474fn testUnwindInfo(b: *Build, opts: Options) *Step {2572fn testUnwindInfo(b: *Build, opts: Options) *Step {
2475 const test_step = addTestStep(b, "unwind-info", opts);2573 const test_step = addTestStep(b, "unwind-info", opts);
24762574