authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2024-07-09 11:22:24+02:00
committergravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2024-07-18 09:13:08+02:00
logb9bac32a2562985ca7a67877169343975fd8f851
treebf0f26530abc99fee00ba73d2e0a9de1e27f8dfc
parentc59583e43de35e91ac194860cd1eb63e61c272aa

macho: migrate Atom and Symbol


5 files changed, 266 insertions(+), 163 deletions(-)

src/link/MachO.zig+139-32
......@@ -22,16 +22,10 @@ dylibs: std.ArrayListUnmanaged(File.Index) = .{},
2222segments: std.ArrayListUnmanaged(macho.segment_command_64) = .{},
2323sections: std.MultiArrayList(Section) = .{},
2424
25symbols: std.ArrayListUnmanaged(Symbol) = .{},
26symbols_extra: std.ArrayListUnmanaged(u32) = .{},
27symbols_free_list: std.ArrayListUnmanaged(Symbol.Index) = .{},
28globals: std.AutoArrayHashMapUnmanaged(u32, Symbol.Index) = .{},
25resolver: SymbolResolver = .{},
2926/// This table will be populated after `scanRelocs` has run.
3027/// Key is symbol index.
31undefs: std.AutoHashMapUnmanaged(Symbol.Index, std.ArrayListUnmanaged(Atom.Index)) = .{},
32/// Global symbols we need to resolve for the link to succeed.
33undefined_symbols: std.ArrayListUnmanaged(Symbol.Index) = .{},
34boundary_symbols: std.ArrayListUnmanaged(Symbol.Index) = .{},
28undefs: std.AutoHashMapUnmanaged(Ref, std.ArrayListUnmanaged(Ref)) = .{},
3529
3630dyld_info_cmd: macho.dyld_info_command = .{},
3731symtab_cmd: macho.symtab_command = .{},
......@@ -55,19 +49,8 @@ eh_frame_sect_index: ?u8 = null,
5549unwind_info_sect_index: ?u8 = null,
5650objc_stubs_sect_index: ?u8 = null,
5751
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
6652thunks: std.ArrayListUnmanaged(Thunk) = .{},
6753
68/// String interning table
69strings: StringTable = .{},
70
7154/// Output synthetic sections
7255symtab: std.ArrayListUnmanaged(macho.nlist_64) = .{},
7356strtab: std.ArrayListUnmanaged(u8) = .{},
......@@ -4196,7 +4179,7 @@ const Section = struct {
41964179pub const LiteralPool = struct {
41974180 table: std.AutoArrayHashMapUnmanaged(void, void) = .{},
41984181 keys: std.ArrayListUnmanaged(Key) = .{},
4199 values: std.ArrayListUnmanaged(Atom.Index) = .{},
4182 values: std.ArrayListUnmanaged(MachO.Ref) = .{},
42004183 data: std.ArrayListUnmanaged(u8) = .{},
42014184
42024185 pub fn deinit(lp: *LiteralPool, allocator: Allocator) void {
......@@ -4206,17 +4189,21 @@ pub const LiteralPool = struct {
42064189 lp.data.deinit(allocator);
42074190 }
42084191
4209 pub fn getAtom(lp: LiteralPool, index: Index, macho_file: *MachO) *Atom {
4210 assert(index < lp.values.items.len);
4211 return macho_file.getAtom(lp.values.items[index]).?;
4212 }
4213
42144192 const InsertResult = struct {
42154193 found_existing: bool,
42164194 index: Index,
4217 atom: *Atom.Index,
4195 ref: *MachO.Ref,
42184196 };
42194197
4198 pub fn getSymbolRef(lp: LiteralPool, index: Index) MachO.Ref {
4199 assert(index < lp.values.items.len);
4200 return lp.values.items[index];
4201 }
4202
4203 pub fn getSymbol(lp: LiteralPool, index: Index, macho_file: *MachO) *Symbol {
4204 return lp.getSymbolRef(index).getSymbol(macho_file).?;
4205 }
4206
42204207 pub fn insert(lp: *LiteralPool, allocator: Allocator, @"type": u8, string: []const u8) !InsertResult {
42214208 const size: u32 = @intCast(string.len);
42224209 try lp.data.ensureUnusedCapacity(allocator, size);
......@@ -4278,12 +4265,6 @@ const HotUpdateState = struct {
42784265 mach_task: ?std.c.MachTask = null,
42794266};
42804267
4281pub const DynamicRelocs = struct {
4282 rebase_relocs: u32 = 0,
4283 bind_relocs: u32 = 0,
4284 weak_bind_relocs: u32 = 0,
4285};
4286
42874268pub const SymtabCtx = struct {
42884269 ilocal: u32 = 0,
42894270 istab: u32 = 0,
......@@ -4293,6 +4274,7 @@ pub const SymtabCtx = struct {
42934274 nstabs: u32 = 0,
42944275 nexports: u32 = 0,
42954276 nimports: u32 = 0,
4277 stroff: u32 = 0,
42964278 strsize: u32 = 0,
42974279};
42984280
......@@ -4579,6 +4561,131 @@ const UndefinedTreatment = enum {
45794561 dynamic_lookup,
45804562};
45814563
4564/// A reference to atom or symbol in an input file.
4565/// If file == 0, symbol is an undefined global.
4566pub const Ref = struct {
4567 index: u32,
4568 file: File.Index,
4569
4570 pub fn eql(ref: Ref, other: Ref) bool {
4571 return ref.index == other.index and ref.file == other.file;
4572 }
4573
4574 pub fn getFile(ref: Ref, macho_file: *MachO) ?File {
4575 return macho_file.getFile(ref.file);
4576 }
4577
4578 pub fn getAtom(ref: Ref, macho_file: *MachO) ?*Atom {
4579 const file = ref.getFile(macho_file) orelse return null;
4580 return file.getAtom(ref.index);
4581 }
4582
4583 pub fn getSymbol(ref: Ref, macho_file: *MachO) ?*Symbol {
4584 const file = ref.getFile(macho_file) orelse return null;
4585 return switch (file) {
4586 inline else => |x| &x.symbols.items[ref.index],
4587 };
4588 }
4589
4590 pub fn format(
4591 ref: Ref,
4592 comptime unused_fmt_string: []const u8,
4593 options: std.fmt.FormatOptions,
4594 writer: anytype,
4595 ) !void {
4596 _ = unused_fmt_string;
4597 _ = options;
4598 try writer.print("%{d} in file({d})", .{ ref.index, ref.file });
4599 }
4600};
4601
4602pub const SymbolResolver = struct {
4603 keys: std.ArrayListUnmanaged(Key) = .{},
4604 values: std.ArrayListUnmanaged(Ref) = .{},
4605 table: std.AutoArrayHashMapUnmanaged(void, void) = .{},
4606
4607 const Result = struct {
4608 found_existing: bool,
4609 index: Index,
4610 ref: *Ref,
4611 };
4612
4613 pub fn deinit(resolver: *SymbolResolver, allocator: Allocator) void {
4614 resolver.keys.deinit(allocator);
4615 resolver.values.deinit(allocator);
4616 resolver.table.deinit(allocator);
4617 }
4618
4619 pub fn getOrPut(
4620 resolver: *SymbolResolver,
4621 allocator: Allocator,
4622 ref: Ref,
4623 macho_file: *MachO,
4624 ) !Result {
4625 const adapter = Adapter{ .keys = resolver.keys.items, .macho_file = macho_file };
4626 const key = Key{ .index = ref.index, .file = ref.file };
4627 const gop = try resolver.table.getOrPutAdapted(allocator, key, adapter);
4628 if (!gop.found_existing) {
4629 try resolver.keys.append(allocator, key);
4630 _ = try resolver.values.addOne(allocator);
4631 }
4632 return .{
4633 .found_existing = gop.found_existing,
4634 .index = @intCast(gop.index + 1),
4635 .ref = &resolver.values.items[gop.index],
4636 };
4637 }
4638
4639 pub fn get(resolver: SymbolResolver, index: Index) ?Ref {
4640 if (index == 0) return null;
4641 return resolver.values.items[index - 1];
4642 }
4643
4644 pub fn reset(resolver: *SymbolResolver) void {
4645 resolver.keys.clearRetainingCapacity();
4646 resolver.values.clearRetainingCapacity();
4647 resolver.table.clearRetainingCapacity();
4648 }
4649
4650 const Key = struct {
4651 index: Symbol.Index,
4652 file: File.Index,
4653
4654 fn getName(key: Key, macho_file: *MachO) [:0]const u8 {
4655 const ref = Ref{ .index = key.index, .file = key.file };
4656 return ref.getSymbol(macho_file).?.getName(macho_file);
4657 }
4658
4659 fn eql(key: Key, other: Key, macho_file: *MachO) bool {
4660 const key_name = key.getName(macho_file);
4661 const other_name = other.getName(macho_file);
4662 return mem.eql(u8, key_name, other_name);
4663 }
4664
4665 fn hash(key: Key, macho_file: *MachO) u32 {
4666 const name = key.getName(macho_file);
4667 return @truncate(Hash.hash(0, name));
4668 }
4669 };
4670
4671 const Adapter = struct {
4672 keys: []const Key,
4673 macho_file: *MachO,
4674
4675 pub fn eql(ctx: @This(), key: Key, b_void: void, b_map_index: usize) bool {
4676 _ = b_void;
4677 const other = ctx.keys[b_map_index];
4678 return key.eql(other, ctx.macho_file);
4679 }
4680
4681 pub fn hash(ctx: @This(), key: Key) u32 {
4682 return key.hash(ctx.macho_file);
4683 }
4684 };
4685
4686 pub const Index = u32;
4687};
4688
45824689const MachO = @This();
45834690
45844691const std = @import("std");
src/link/MachO/Atom.zig+74-92
......@@ -47,16 +47,6 @@ pub fn getFile(self: Atom, macho_file: *MachO) File {
4747 return macho_file.getFile(self.file).?;
4848}
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
6050pub fn getRelocs(self: Atom, macho_file: *MachO) []const Relocation {
6151 return switch (self.getFile(macho_file)) {
6252 .dylib => unreachable,
......@@ -88,8 +78,7 @@ pub fn getPriority(self: Atom, macho_file: *MachO) u64 {
8878}
8979
9080pub fn getUnwindRecords(self: Atom, macho_file: *MachO) []const UnwindInfo.Record.Index {
91 if (!self.flags.unwind) return &[0]UnwindInfo.Record.Index{};
92 const extra = self.getExtra(macho_file).?;
81 const extra = self.getExtra(macho_file);
9382 return switch (self.getFile(macho_file)) {
9483 .dylib => unreachable,
9584 .zig_object, .internal => &[0]UnwindInfo.Record.Index{},
......@@ -110,44 +99,39 @@ pub fn markUnwindRecordsDead(self: Atom, macho_file: *MachO) void {
11099}
111100
112101pub fn getThunk(self: Atom, macho_file: *MachO) *Thunk {
113 assert(self.flags.thunk);
114 const extra = self.getExtra(macho_file).?;
102 const extra = self.getExtra(macho_file);
115103 return macho_file.getThunk(extra.thunk);
116104}
117105
118pub fn getLiteralPoolIndex(self: Atom, macho_file: *MachO) ?MachO.LiteralPool.Index {
119 if (!self.flags.literal_pool) return null;
120 return self.getExtra(macho_file).?.literal_index;
121}
122
123106const AddExtraOpts = struct {
124107 thunk: ?u32 = null,
125108 rel_index: ?u32 = null,
126109 rel_count: ?u32 = null,
110 rel_out_index: ?u32 = null,
111 rel_out_count: ?u32 = null,
127112 unwind_index: ?u32 = null,
128113 unwind_count: ?u32 = null,
129 literal_index: ?u32 = null,
114 literal_pool_index: ?u32 = null,
115 literal_symbol_index: ?u32 = null,
130116};
131117
132pub fn addExtra(atom: *Atom, opts: AddExtraOpts, macho_file: *MachO) !void {
133 if (atom.getExtra(macho_file) == null) {
134 atom.extra = try macho_file.addAtomExtra(.{});
135 }
136 var extra = atom.getExtra(macho_file).?;
118pub fn addExtra(atom: *Atom, opts: AddExtraOpts, macho_file: *MachO) void {
119 const file = atom.getFile(macho_file);
120 var extra = file.getAtomExtra(atom.extra);
137121 inline for (@typeInfo(@TypeOf(opts)).Struct.fields) |field| {
138122 if (@field(opts, field.name)) |x| {
139123 @field(extra, field.name) = x;
140124 }
141125 }
142 atom.setExtra(extra, macho_file);
126 file.setAtomExtra(atom.extra, extra);
143127}
144128
145pub inline fn getExtra(atom: Atom, macho_file: *MachO) ?Extra {
146 return macho_file.getAtomExtra(atom.extra);
129pub inline fn getExtra(atom: Atom, macho_file: *MachO) Extra {
130 return atom.getFile(macho_file).getAtomExtra(atom.extra);
147131}
148132
149133pub inline fn setExtra(atom: Atom, extra: Extra, macho_file: *MachO) void {
150 macho_file.setAtomExtra(atom.extra, extra);
134 atom.getFile(macho_file).setAtomExtra(atom.extra, extra);
151135}
152136
153137pub fn initOutputSection(sect: macho.section_64, macho_file: *MachO) !u8 {
......@@ -467,7 +451,7 @@ pub fn scanRelocs(self: Atom, macho_file: *MachO) !void {
467451
468452 switch (rel.type) {
469453 .branch => {
470 const symbol = rel.getTargetSymbol(macho_file);
454 const symbol = rel.getTargetSymbol(self, macho_file);
471455 if (symbol.flags.import or (symbol.flags.@"export" and symbol.flags.weak) or symbol.flags.interposable) {
472456 symbol.flags.stubs = true;
473457 if (symbol.flags.weak) {
......@@ -482,7 +466,7 @@ pub fn scanRelocs(self: Atom, macho_file: *MachO) !void {
482466 .got_load_page,
483467 .got_load_pageoff,
484468 => {
485 const symbol = rel.getTargetSymbol(macho_file);
469 const symbol = rel.getTargetSymbol(self, macho_file);
486470 if (symbol.flags.import or
487471 (symbol.flags.@"export" and symbol.flags.weak) or
488472 symbol.flags.interposable or
......@@ -496,18 +480,18 @@ pub fn scanRelocs(self: Atom, macho_file: *MachO) !void {
496480 },
497481
498482 .zig_got_load => {
499 assert(rel.getTargetSymbol(macho_file).flags.has_zig_got);
483 assert(rel.getTargetSymbol(self, macho_file).flags.has_zig_got);
500484 },
501485
502486 .got => {
503 rel.getTargetSymbol(macho_file).flags.needs_got = true;
487 rel.getTargetSymbol(self, macho_file).flags.needs_got = true;
504488 },
505489
506490 .tlv,
507491 .tlvp_page,
508492 .tlvp_pageoff,
509493 => {
510 const symbol = rel.getTargetSymbol(macho_file);
494 const symbol = rel.getTargetSymbol(self, macho_file);
511495 if (!symbol.flags.tlv) {
512496 try macho_file.reportParseError2(
513497 self.getFile(macho_file).getIndex(),
......@@ -526,7 +510,7 @@ pub fn scanRelocs(self: Atom, macho_file: *MachO) !void {
526510 .unsigned => {
527511 if (rel.meta.length == 3) { // TODO this really should check if this is pointer width
528512 if (rel.tag == .@"extern") {
529 const symbol = rel.getTargetSymbol(macho_file);
513 const symbol = rel.getTargetSymbol(self, macho_file);
530514 if (symbol.isTlvInit(macho_file)) {
531515 macho_file.has_tlv = true;
532516 continue;
......@@ -559,14 +543,15 @@ pub fn scanRelocs(self: Atom, macho_file: *MachO) !void {
559543fn reportUndefSymbol(self: Atom, rel: Relocation, macho_file: *MachO) !bool {
560544 if (rel.tag == .local) return false;
561545
562 const sym = rel.getTargetSymbol(macho_file);
563 if (sym.getFile(macho_file) == null) {
546 const file = self.getFile(macho_file);
547 const ref = file.getSymbolRef(rel.target, macho_file);
548 if (ref.getFile(macho_file) == null) {
564549 const gpa = macho_file.base.comp.gpa;
565 const gop = try macho_file.undefs.getOrPut(gpa, rel.target);
550 const gop = try macho_file.undefs.getOrPut(gpa, .{ .index = rel.target, .file = self.file });
566551 if (!gop.found_existing) {
567552 gop.value_ptr.* = .{};
568553 }
569 try gop.value_ptr.append(gpa, self.atom_index);
554 try gop.value_ptr.append(gpa, .{ .index = self.atom_index, .file = self.file });
570555 return true;
571556 }
572557
......@@ -582,7 +567,7 @@ pub fn resolveRelocs(self: Atom, macho_file: *MachO, buffer: []u8) !void {
582567 const name = self.getName(macho_file);
583568 const relocs = self.getRelocs(macho_file);
584569
585 relocs_log.debug("{x}: {s}", .{ self.getAddress(macho_file), name });
570 relocs_log.debug("{x}: {s}", .{ self.value, name });
586571
587572 var has_error = false;
588573 var stream = std.io.fixedBufferStream(buffer);
......@@ -593,7 +578,7 @@ pub fn resolveRelocs(self: Atom, macho_file: *MachO, buffer: []u8) !void {
593578 const subtractor = if (rel.meta.has_subtractor) relocs[i - 1] else null;
594579
595580 if (rel.tag == .@"extern") {
596 if (rel.getTargetSymbol(macho_file).getFile(macho_file) == null) continue;
581 if (rel.getTargetSymbol(self, macho_file).getFile(macho_file) == null) continue;
597582 }
598583
599584 try stream.seekTo(rel_offset);
......@@ -601,8 +586,8 @@ pub fn resolveRelocs(self: Atom, macho_file: *MachO, buffer: []u8) !void {
601586 switch (err) {
602587 error.RelaxFail => {
603588 const target = switch (rel.tag) {
604 .@"extern" => rel.getTargetSymbol(macho_file).getName(macho_file),
605 .local => rel.getTargetAtom(macho_file).getName(macho_file),
589 .@"extern" => rel.getTargetSymbol(self, macho_file).getName(macho_file),
590 .local => rel.getTargetAtom(self, macho_file).getName(macho_file),
606591 };
607592 try macho_file.reportParseError2(
608593 file.getIndex(),
......@@ -642,12 +627,12 @@ fn resolveRelocInner(
642627 const rel_offset = math.cast(usize, rel.offset - self.off) orelse return error.Overflow;
643628 const P = @as(i64, @intCast(self.getAddress(macho_file))) + @as(i64, @intCast(rel_offset));
644629 const A = rel.addend + rel.getRelocAddend(cpu_arch);
645 const S: i64 = @intCast(rel.getTargetAddress(macho_file));
646 const G: i64 = @intCast(rel.getGotTargetAddress(macho_file));
630 const S: i64 = @intCast(rel.getTargetAddress(self, macho_file));
631 const G: i64 = @intCast(rel.getGotTargetAddress(self, macho_file));
647632 const TLS = @as(i64, @intCast(macho_file.getTlsAddress()));
648 const SUB = if (subtractor) |sub| @as(i64, @intCast(sub.getTargetAddress(macho_file))) else 0;
633 const SUB = if (subtractor) |sub| @as(i64, @intCast(sub.getTargetAddress(self, macho_file))) else 0;
649634 // Address of the __got_zig table entry if any.
650 const ZIG_GOT = @as(i64, @intCast(rel.getZigGotTargetAddress(macho_file)));
635 const ZIG_GOT = @as(i64, @intCast(rel.getZigGotTargetAddress(self, macho_file)));
651636
652637 const divExact = struct {
653638 fn divExact(atom: Atom, r: Relocation, num: u12, den: u12, ctx: *MachO) !u12 {
......@@ -668,7 +653,7 @@ fn resolveRelocInner(
668653 rel_offset,
669654 @tagName(rel.type),
670655 S + A - SUB,
671 rel.getTargetAtom(macho_file).atom_index,
656 rel.getTargetAtom(self, macho_file).atom_index,
672657 }),
673658 .@"extern" => relocs_log.debug(" {x}<+{d}>: {s}: [=> {x}] G({x}) ZG({x}) ({s})", .{
674659 P,
......@@ -677,7 +662,7 @@ fn resolveRelocInner(
677662 S + A - SUB,
678663 G + A,
679664 ZIG_GOT + A,
680 rel.getTargetSymbol(macho_file).getName(macho_file),
665 rel.getTargetSymbol(self, macho_file).getName(macho_file),
681666 }),
682667 }
683668
......@@ -688,7 +673,7 @@ fn resolveRelocInner(
688673 assert(!rel.meta.pcrel);
689674 if (rel.meta.length == 3) {
690675 if (rel.tag == .@"extern") {
691 const sym = rel.getTargetSymbol(macho_file);
676 const sym = rel.getTargetSymbol(self, macho_file);
692677 if (sym.isTlvInit(macho_file)) {
693678 try writer.writeInt(u64, @intCast(S - TLS), .little);
694679 return;
......@@ -718,7 +703,7 @@ fn resolveRelocInner(
718703 .aarch64 => {
719704 const disp: i28 = math.cast(i28, S + A - P) orelse blk: {
720705 const thunk = self.getThunk(macho_file);
721 const S_: i64 = @intCast(thunk.getTargetAddress(rel.target, macho_file));
706 const S_: i64 = @intCast(thunk.getTargetAddress(rel.getTargetSymbolRef(self, macho_file), macho_file));
722707 break :blk math.cast(i28, S_ + A - P) orelse return error.Overflow;
723708 };
724709 aarch64.writeBranchImm(disp, code[rel_offset..][0..4]);
......@@ -731,7 +716,7 @@ fn resolveRelocInner(
731716 assert(rel.tag == .@"extern");
732717 assert(rel.meta.length == 2);
733718 assert(rel.meta.pcrel);
734 if (rel.getTargetSymbol(macho_file).flags.has_got) {
719 if (rel.getTargetSymbol(self, macho_file).flags.has_got) {
735720 try writer.writeInt(i32, @intCast(G + A - P), .little);
736721 } else {
737722 try x86_64.relaxGotLoad(self, code[rel_offset - 3 ..], rel, macho_file);
......@@ -754,7 +739,7 @@ fn resolveRelocInner(
754739 assert(rel.tag == .@"extern");
755740 assert(rel.meta.length == 2);
756741 assert(rel.meta.pcrel);
757 const sym = rel.getTargetSymbol(macho_file);
742 const sym = rel.getTargetSymbol(self, macho_file);
758743 if (sym.flags.tlv_ptr) {
759744 const S_: i64 = @intCast(sym.getTlvPtrAddress(macho_file));
760745 try writer.writeInt(i32, @intCast(S_ + A - P), .little);
......@@ -777,7 +762,7 @@ fn resolveRelocInner(
777762 assert(rel.tag == .@"extern");
778763 assert(rel.meta.length == 2);
779764 assert(rel.meta.pcrel);
780 const sym = rel.getTargetSymbol(macho_file);
765 const sym = rel.getTargetSymbol(self, macho_file);
781766 const source = math.cast(u64, P) orelse return error.Overflow;
782767 const target = target: {
783768 const target = switch (rel.type) {
......@@ -836,7 +821,7 @@ fn resolveRelocInner(
836821 assert(rel.meta.length == 2);
837822 assert(!rel.meta.pcrel);
838823
839 const sym = rel.getTargetSymbol(macho_file);
824 const sym = rel.getTargetSymbol(self, macho_file);
840825 const target = target: {
841826 const target = if (sym.flags.tlv_ptr) blk: {
842827 const S_: i64 = @intCast(sym.getTlvPtrAddress(macho_file));
......@@ -980,48 +965,47 @@ pub fn calcNumRelocs(self: Atom, macho_file: *MachO) u32 {
980965 }
981966}
982967
983pub fn writeRelocs(self: Atom, macho_file: *MachO, code: []u8, buffer: *std.ArrayList(macho.relocation_info)) !void {
968pub fn writeRelocs(self: Atom, macho_file: *MachO, code: []u8, buffer: []macho.relocation_info) !void {
984969 const tracy = trace(@src());
985970 defer tracy.end();
986971
987972 const cpu_arch = macho_file.getTarget().cpu.arch;
988973 const relocs = self.getRelocs(macho_file);
989 var stream = std.io.fixedBufferStream(code);
990974
975 var i: usize = 0;
991976 for (relocs) |rel| {
977 defer i += 1;
992978 const rel_offset = rel.offset - self.off;
993979 const r_address: i32 = math.cast(i32, self.value + rel_offset) orelse return error.Overflow;
994980 const r_symbolnum = r_symbolnum: {
995981 const r_symbolnum: u32 = switch (rel.tag) {
996 .local => rel.getTargetAtom(macho_file).out_n_sect + 1,
997 .@"extern" => rel.getTargetSymbol(macho_file).getOutputSymtabIndex(macho_file).?,
982 .local => rel.getTargetAtom(self, macho_file).out_n_sect + 1,
983 .@"extern" => rel.getTargetSymbol(self, macho_file).getOutputSymtabIndex(macho_file).?,
998984 };
999985 break :r_symbolnum math.cast(u24, r_symbolnum) orelse return error.Overflow;
1000986 };
1001987 const r_extern = rel.tag == .@"extern";
1002988 var addend = rel.addend + rel.getRelocAddend(cpu_arch);
1003989 if (rel.tag == .local) {
1004 const target: i64 = @intCast(rel.getTargetAddress(macho_file));
990 const target: i64 = @intCast(rel.getTargetAddress(self, macho_file));
1005991 addend += target;
1006992 }
1007993
1008 try stream.seekTo(rel_offset);
1009
1010994 switch (cpu_arch) {
1011995 .aarch64 => {
1012996 if (rel.type == .unsigned) switch (rel.meta.length) {
1013997 0, 1 => unreachable,
1014 2 => try stream.writer().writeInt(i32, @truncate(addend), .little),
1015 3 => try stream.writer().writeInt(i64, addend, .little),
998 2 => mem.writeInt(i32, code[rel_offset..][0..4], @truncate(addend), .little),
999 3 => mem.writeInt(i64, code[rel_offset..][0..8], addend, .little),
10161000 } else if (addend > 0) {
1017 buffer.appendAssumeCapacity(.{
1001 buffer[i] = .{
10181002 .r_address = r_address,
10191003 .r_symbolnum = @bitCast(math.cast(i24, addend) orelse return error.Overflow),
10201004 .r_pcrel = 0,
10211005 .r_length = 2,
10221006 .r_extern = 0,
10231007 .r_type = @intFromEnum(macho.reloc_type_arm64.ARM64_RELOC_ADDEND),
1024 });
1008 };
10251009 }
10261010
10271011 const r_type: macho.reloc_type_arm64 = switch (rel.type) {
......@@ -1045,14 +1029,14 @@ pub fn writeRelocs(self: Atom, macho_file: *MachO, code: []u8, buffer: *std.Arra
10451029 .tlv,
10461030 => unreachable,
10471031 };
1048 buffer.appendAssumeCapacity(.{
1032 buffer[i] = .{
10491033 .r_address = r_address,
10501034 .r_symbolnum = r_symbolnum,
10511035 .r_pcrel = @intFromBool(rel.meta.pcrel),
10521036 .r_extern = @intFromBool(r_extern),
10531037 .r_length = rel.meta.length,
10541038 .r_type = @intFromEnum(r_type),
1055 });
1039 };
10561040 },
10571041 .x86_64 => {
10581042 if (rel.meta.pcrel) {
......@@ -1064,8 +1048,8 @@ pub fn writeRelocs(self: Atom, macho_file: *MachO, code: []u8, buffer: *std.Arra
10641048 }
10651049 switch (rel.meta.length) {
10661050 0, 1 => unreachable,
1067 2 => try stream.writer().writeInt(i32, @truncate(addend), .little),
1068 3 => try stream.writer().writeInt(i64, addend, .little),
1051 2 => mem.writeInt(i32, code[rel_offset..][0..4], @truncate(addend), .little),
1052 3 => mem.writeInt(i64, code[rel_offset..][0..8], addend, .little),
10691053 }
10701054
10711055 const r_type: macho.reloc_type_x86_64 = switch (rel.type) {
......@@ -1089,18 +1073,20 @@ pub fn writeRelocs(self: Atom, macho_file: *MachO, code: []u8, buffer: *std.Arra
10891073 .tlvp_pageoff,
10901074 => unreachable,
10911075 };
1092 buffer.appendAssumeCapacity(.{
1076 buffer[i] = .{
10931077 .r_address = r_address,
10941078 .r_symbolnum = r_symbolnum,
10951079 .r_pcrel = @intFromBool(rel.meta.pcrel),
10961080 .r_extern = @intFromBool(r_extern),
10971081 .r_length = rel.meta.length,
10981082 .r_type = @intFromEnum(r_type),
1099 });
1083 };
11001084 },
11011085 else => unreachable,
11021086 }
11031087 }
1088
1089 assert(i == buffer.len);
11041090}
11051091
11061092pub fn format(
......@@ -1139,16 +1125,15 @@ fn format2(
11391125 const atom = ctx.atom;
11401126 const macho_file = ctx.macho_file;
11411127 const file = atom.getFile(macho_file);
1142 try writer.print("atom({d}) : {s} : @{x} : sect({d}) : align({x}) : size({x}) : nreloc({d})", .{
1143 atom.atom_index, atom.getName(macho_file), atom.getAddress(macho_file),
1144 atom.out_n_sect, atom.alignment, atom.size,
1145 atom.getRelocs(macho_file).len,
1128 try writer.print("atom({d}) : {s} : @{x} : sect({d}) : align({x}) : size({x}) : nreloc({d}) : thunk({d})", .{
1129 atom.atom_index, atom.getName(macho_file), atom.getAddress(macho_file),
1130 atom.out_n_sect, atom.alignment, atom.size,
1131 atom.getRelocs(macho_file).len, atom.getExtra(macho_file).thunk,
11461132 });
1147 if (atom.flags.thunk) try writer.print(" : thunk({d})", .{atom.getExtra(macho_file).?.thunk});
11481133 if (!atom.flags.alive) try writer.writeAll(" : [*]");
1149 if (atom.flags.unwind) {
1134 if (atom.getUnwindRecords(macho_file).len > 0) {
11501135 try writer.writeAll(" : unwind{ ");
1151 const extra = atom.getExtra(macho_file).?;
1136 const extra = atom.getExtra(macho_file);
11521137 for (atom.getUnwindRecords(macho_file), extra.unwind_index..) |index, i| {
11531138 const rec = file.object.getUnwindRecord(index);
11541139 try writer.print("{d}", .{index});
......@@ -1167,18 +1152,6 @@ pub const Flags = packed struct {
11671152
11681153 /// Specifies if this atom has been visited during garbage collection.
11691154 visited: bool = false,
1170
1171 /// Whether this atom has a range extension thunk.
1172 thunk: bool = false,
1173
1174 /// Whether this atom has any relocations.
1175 relocs: bool = false,
1176
1177 /// Whether this atom has any unwind records.
1178 unwind: bool = false,
1179
1180 /// Whether this atom has LiteralPool entry.
1181 literal_pool: bool = false,
11821155};
11831156
11841157pub const Extra = struct {
......@@ -1191,6 +1164,12 @@ pub const Extra = struct {
11911164 /// Count of relocations belonging to this atom.
11921165 rel_count: u32 = 0,
11931166
1167 /// Start index of relocations being written out to file for this atom.
1168 rel_out_index: u32 = 0,
1169
1170 /// Count of relocations written out to file for this atom.
1171 rel_out_count: u32 = 0,
1172
11941173 /// Start index of relocations belonging to this atom.
11951174 unwind_index: u32 = 0,
11961175
......@@ -1198,7 +1177,10 @@ pub const Extra = struct {
11981177 unwind_count: u32 = 0,
11991178
12001179 /// Index into LiteralPool entry for this atom.
1201 literal_index: u32 = 0,
1180 literal_pool_index: u32 = 0,
1181
1182 /// Index into the File's symbol table for local symbol representing this literal atom.
1183 literal_symbol_index: u32 = 0,
12021184};
12031185
12041186pub const Alignment = @import("../../InternPool.zig").Alignment;
src/link/MachO/InternalObject.zig+6
......@@ -639,6 +639,12 @@ pub fn asFile(self: *InternalObject) File {
639639 return .{ .internal = self };
640640}
641641
642pub fn getAtomRelocs(self: *const InternalObject, atom: Atom, macho_file: *MachO) []const Relocation {
643 const extra = atom.getExtra(macho_file).?;
644 const relocs = self.sections.items(.relocs)[atom.n_sect];
645 return relocs.items[extra.rel_index..][0..extra.rel_count];
646}
647
642648fn addAtom(self: *InternalObject, allocator: Allocator) !Atom.Index {
643649 const atom_index: Atom.Index = @intCast(self.atoms.items.len);
644650 const atom = try self.atoms.addOne(allocator);
src/link/MachO/Object.zig+10-4
......@@ -1860,7 +1860,7 @@ pub fn calcStabsSize(self: *Object, macho_file: *MachO) error{Overflow}!void {
18601860 const name = sym.getName(macho_file);
18611861 if (name.len > 0 and (name[0] == 'L' or name[0] == 'l')) continue;
18621862 }
1863 const sect = macho_file.sections.items(.header)[sym.out_n_sect];
1863 const sect = macho_file.sections.items(.header)[sym.getOutputSectionIndex(macho_file)];
18641864 if (sect.isCode()) {
18651865 self.output_symtab_ctx.nstabs += 4; // N_BNSYM, N_FUN, N_FUN, N_ENSYM
18661866 } else if (sym.visibility == .global) {
......@@ -2198,13 +2198,13 @@ pub fn writeStabs(self: *const Object, stroff: u32, macho_file: *MachO) void {
21982198 const name = sym.getName(macho_file);
21992199 if (name.len > 0 and (name[0] == 'L' or name[0] == 'l')) continue;
22002200 }
2201 const sect = macho_file.sections.items(.header)[sym.out_n_sect];
2201 const sect = macho_file.sections.items(.header)[sym.getOutputSectionIndex(macho_file)];
22022202 const sym_n_strx = n_strx: {
22032203 const symtab_index = sym.getOutputSymtabIndex(macho_file).?;
22042204 const osym = macho_file.symtab.items[symtab_index];
22052205 break :n_strx osym.n_strx;
22062206 };
2207 const sym_n_sect: u8 = if (!sym.flags.abs) @intCast(sym.out_n_sect + 1) else 0;
2207 const sym_n_sect: u8 = if (!sym.flags.abs) @intCast(sym.getOutputSectionIndex(macho_file) + 1) else 0;
22082208 const sym_n_value = sym.getAddress(.{}, macho_file);
22092209 const sym_size = sym.getSize(macho_file);
22102210 if (sect.isCode()) {
......@@ -2299,7 +2299,7 @@ pub fn writeStabs(self: *const Object, stroff: u32, macho_file: *MachO) void {
22992299 const osym = macho_file.symtab.items[symtab_index];
23002300 break :n_strx osym.n_strx;
23012301 };
2302 const sym_n_sect: u8 = if (!sym.flags.abs) @intCast(sym.out_n_sect + 1) else 0;
2302 const sym_n_sect: u8 = if (!sym.flags.abs) @intCast(sym.getOutputSectionIndex(macho_file) + 1) else 0;
23032303 const sym_n_value = sym.getAddress(.{}, macho_file);
23042304 const sym_size = sym.getSize(macho_file);
23052305 if (stab.is_func) {
......@@ -2340,6 +2340,12 @@ pub fn writeStabs(self: *const Object, stroff: u32, macho_file: *MachO) void {
23402340 }
23412341}
23422342
2343pub fn getAtomRelocs(self: *const Object, atom: Atom, macho_file: *MachO) []const Relocation {
2344 const extra = atom.getExtra(macho_file).?;
2345 const relocs = self.sections.items(.relocs)[atom.n_sect];
2346 return relocs.items[extra.rel_index..][0..extra.rel_count];
2347}
2348
23432349fn addString(self: *Object, allocator: Allocator, name: [:0]const u8) error{OutOfMemory}!u32 {
23442350 const off: u32 = @intCast(self.strtab.items.len);
23452351 try self.strtab.ensureUnusedCapacity(allocator, name.len + 1);
src/link/MachO/Symbol.zig+37-35
......@@ -9,17 +9,16 @@ name: u32 = 0,
99/// File where this symbol is defined.
1010file: File.Index = 0,
1111
12/// Atom containing this symbol if any.
13/// Index of 0 means there is no associated atom with this symbol.
12/// Reference to Atom containing this symbol if any.
1413/// Use `getAtom` to get the pointer to the atom.
15atom: Atom.Index = 0,
14atom_ref: MachO.Ref = .{ .index = 0, .file = 0 },
1615
1716/// Assigned output section index for this symbol.
1817out_n_sect: u8 = 0,
1918
2019/// Index of the source nlist this symbol references.
2120/// Use `getNlist` to pull the nlist from the relevant file.
22nlist_idx: Index = 0,
21nlist_idx: u32 = 0,
2322
2423/// Misc flags for the symbol packaged as packed struct for compression.
2524flags: Flags = .{},
......@@ -55,16 +54,19 @@ pub fn weakRef(symbol: Symbol, macho_file: *MachO) bool {
5554}
5655
5756pub fn getName(symbol: Symbol, macho_file: *MachO) [:0]const u8 {
58 if (symbol.flags.global) return macho_file.strings.getAssumeExists(symbol.name);
5957 return switch (symbol.getFile(macho_file).?) {
60 .dylib => unreachable, // There are no local symbols for dylibs
6158 .zig_object => |x| x.strtab.getAssumeExists(symbol.name),
6259 inline else => |x| x.getString(symbol.name),
6360 };
6461}
6562
6663pub 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;
6870}
6971
7072pub fn getFile(symbol: Symbol, macho_file: *MachO) ?File {
......@@ -75,8 +77,10 @@ pub fn getFile(symbol: Symbol, macho_file: *MachO) ?File {
7577pub fn getNlist(symbol: Symbol, macho_file: *MachO) macho.nlist_64 {
7678 const file = symbol.getFile(macho_file).?;
7779 return switch (file) {
80 .dylib => unreachable,
81 .zig_object => unreachable,
7882 .object => |x| x.symtab.items(.nlist)[symbol.nlist_idx],
79 else => unreachable,
83 .internal => |x| x.symtab.items[symbol.nlist_idx],
8084 };
8185}
8286
......@@ -124,33 +128,35 @@ pub fn getAddress(symbol: Symbol, opts: struct {
124128
125129pub fn getGotAddress(symbol: Symbol, macho_file: *MachO) u64 {
126130 if (!symbol.flags.has_got) return 0;
127 const extra = symbol.getExtra(macho_file).?;
131 const extra = symbol.getExtra(macho_file);
128132 return macho_file.got.getAddress(extra.got, macho_file);
129133}
130134
131135pub fn getStubsAddress(symbol: Symbol, macho_file: *MachO) u64 {
132136 if (!symbol.flags.stubs) return 0;
133 const extra = symbol.getExtra(macho_file).?;
137 const extra = symbol.getExtra(macho_file);
134138 return macho_file.stubs.getAddress(extra.stubs, macho_file);
135139}
136140
137141pub fn getObjcStubsAddress(symbol: Symbol, macho_file: *MachO) u64 {
138142 if (!symbol.flags.objc_stubs) return 0;
139 const extra = symbol.getExtra(macho_file).?;
143 const extra = symbol.getExtra(macho_file);
140144 return macho_file.objc_stubs.getAddress(extra.objc_stubs, macho_file);
141145}
142146
143147pub fn getObjcSelrefsAddress(symbol: Symbol, macho_file: *MachO) u64 {
144148 if (!symbol.flags.objc_stubs) return 0;
145 const extra = symbol.getExtra(macho_file).?;
146 const atom = macho_file.getAtom(extra.objc_selrefs).?;
147 assert(atom.flags.alive);
148 return atom.getAddress(macho_file);
149 const extra = symbol.getExtra(macho_file);
150 const file = symbol.getFile(macho_file).?;
151 return switch (file) {
152 .dylib, .zig_object => unreachable,
153 .object, .internal => |x| x.symbols.items[extra.objc_selrefs].getAddress(.{}, macho_file),
154 };
149155}
150156
151157pub fn getTlvPtrAddress(symbol: Symbol, macho_file: *MachO) u64 {
152158 if (!symbol.flags.tlv_ptr) return 0;
153 const extra = symbol.getExtra(macho_file).?;
159 const extra = symbol.getExtra(macho_file);
154160 return macho_file.tlv_ptr.getAddress(extra.tlv_ptr, macho_file);
155161}
156162
......@@ -162,14 +168,14 @@ const GetOrCreateZigGotEntryResult = struct {
162168pub fn getOrCreateZigGotEntry(symbol: *Symbol, symbol_index: Index, macho_file: *MachO) !GetOrCreateZigGotEntryResult {
163169 assert(!macho_file.base.isRelocatable());
164170 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 };
166172 const index = try macho_file.zig_got.addSymbol(symbol_index, macho_file);
167173 return .{ .found_existing = false, .index = index };
168174}
169175
170176pub fn getZigGotAddress(symbol: Symbol, macho_file: *MachO) u64 {
171177 if (!symbol.flags.has_zig_got) return 0;
172 const extras = symbol.getExtra(macho_file).?;
178 const extras = symbol.getExtra(macho_file);
173179 return macho_file.zig_got.entryAddress(extras.zig_got, macho_file);
174180}
175181
......@@ -202,11 +208,8 @@ const AddExtraOpts = struct {
202208 symtab: ?u32 = null,
203209};
204210
205pub fn addExtra(symbol: *Symbol, opts: AddExtraOpts, macho_file: *MachO) !void {
206 if (symbol.getExtra(macho_file) == null) {
207 symbol.extra = try macho_file.addSymbolExtra(.{});
208 }
209 var extra = symbol.getExtra(macho_file).?;
211pub fn addExtra(symbol: *Symbol, opts: AddExtraOpts, macho_file: *MachO) void {
212 var extra = symbol.getExtra(macho_file);
210213 inline for (@typeInfo(@TypeOf(opts)).Struct.fields) |field| {
211214 if (@field(opts, field.name)) |x| {
212215 @field(extra, field.name) = x;
......@@ -215,18 +218,22 @@ pub fn addExtra(symbol: *Symbol, opts: AddExtraOpts, macho_file: *MachO) !void {
215218 symbol.setExtra(extra, macho_file);
216219}
217220
218pub inline fn getExtra(symbol: Symbol, macho_file: *MachO) ?Extra {
219 return macho_file.getSymbolExtra(symbol.extra);
221pub inline fn getExtra(symbol: Symbol, macho_file: *MachO) Extra {
222 return switch (symbol.getFile(macho_file).?) {
223 inline else => |x| x.getSymbolExtra(symbol.extra),
224 };
220225}
221226
222227pub 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 };
224231}
225232
226233pub fn setOutputSym(symbol: Symbol, macho_file: *MachO, out: *macho.nlist_64) void {
227234 if (symbol.isLocal()) {
228235 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);
230237 out.n_desc = 0;
231238 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
238245 assert(symbol.visibility == .global);
239246 out.n_type = macho.N_EXT;
240247 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);
242249 out.n_value = symbol.getAddress(.{ .stubs = false }, macho_file);
243250 out.n_desc = 0;
244251
......@@ -318,8 +325,8 @@ fn format2(
318325 symbol.getAddress(.{}, ctx.macho_file),
319326 });
320327 if (symbol.getFile(ctx.macho_file)) |file| {
321 if (symbol.out_n_sect != 0) {
322 try writer.print(" : sect({d})", .{symbol.out_n_sect});
328 if (symbol.getOutputSectionIndex(ctx.macho_file) != 0) {
329 try writer.print(" : sect({d})", .{symbol.getOutputSectionIndex(ctx.macho_file)});
323330 }
324331 if (symbol.getAtom(ctx.macho_file)) |atom| {
325332 try writer.print(" : atom({d})", .{atom.atom_index});
......@@ -346,11 +353,6 @@ pub const Flags = packed struct {
346353 /// Whether the symbol is exported at runtime.
347354 @"export": bool = false,
348355
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
354356 /// Whether this symbol is weak.
355357 weak: bool = false,
356358