From b9bac32a2562985ca7a67877169343975fd8f851 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Tue, 9 Jul 2024 11:22:24 +0200 Subject: [PATCH] macho: migrate Atom and Symbol --- src/link/MachO.zig | 171 ++++++++++++++++++++++++------ src/link/MachO/Atom.zig | 166 +++++++++++++---------------- src/link/MachO/InternalObject.zig | 6 ++ src/link/MachO/Object.zig | 14 ++- src/link/MachO/Symbol.zig | 72 +++++++------ 5 files changed, 266 insertions(+), 163 deletions(-) diff --git a/src/link/MachO.zig b/src/link/MachO.zig index 9f7a306a74..06f512e584 100644 --- a/src/link/MachO.zig +++ b/src/link/MachO.zig @@ -22,16 +22,10 @@ dylibs: std.ArrayListUnmanaged(File.Index) = .{}, segments: std.ArrayListUnmanaged(macho.segment_command_64) = .{}, sections: std.MultiArrayList(Section) = .{}, -symbols: std.ArrayListUnmanaged(Symbol) = .{}, -symbols_extra: std.ArrayListUnmanaged(u32) = .{}, -symbols_free_list: std.ArrayListUnmanaged(Symbol.Index) = .{}, -globals: std.AutoArrayHashMapUnmanaged(u32, Symbol.Index) = .{}, +resolver: SymbolResolver = .{}, /// This table will be populated after `scanRelocs` has run. /// Key is symbol index. -undefs: std.AutoHashMapUnmanaged(Symbol.Index, std.ArrayListUnmanaged(Atom.Index)) = .{}, -/// Global symbols we need to resolve for the link to succeed. -undefined_symbols: std.ArrayListUnmanaged(Symbol.Index) = .{}, -boundary_symbols: std.ArrayListUnmanaged(Symbol.Index) = .{}, +undefs: std.AutoHashMapUnmanaged(Ref, std.ArrayListUnmanaged(Ref)) = .{}, dyld_info_cmd: macho.dyld_info_command = .{}, symtab_cmd: macho.symtab_command = .{}, @@ -55,19 +49,8 @@ eh_frame_sect_index: ?u8 = null, unwind_info_sect_index: ?u8 = null, objc_stubs_sect_index: ?u8 = null, -mh_execute_header_index: ?Symbol.Index = null, -mh_dylib_header_index: ?Symbol.Index = null, -dyld_private_index: ?Symbol.Index = null, -dyld_stub_binder_index: ?Symbol.Index = null, -dso_handle_index: ?Symbol.Index = null, -objc_msg_send_index: ?Symbol.Index = null, -entry_index: ?Symbol.Index = null, - thunks: std.ArrayListUnmanaged(Thunk) = .{}, -/// String interning table -strings: StringTable = .{}, - /// Output synthetic sections symtab: std.ArrayListUnmanaged(macho.nlist_64) = .{}, strtab: std.ArrayListUnmanaged(u8) = .{}, @@ -4196,7 +4179,7 @@ const Section = struct { pub const LiteralPool = struct { table: std.AutoArrayHashMapUnmanaged(void, void) = .{}, keys: std.ArrayListUnmanaged(Key) = .{}, - values: std.ArrayListUnmanaged(Atom.Index) = .{}, + values: std.ArrayListUnmanaged(MachO.Ref) = .{}, data: std.ArrayListUnmanaged(u8) = .{}, pub fn deinit(lp: *LiteralPool, allocator: Allocator) void { @@ -4206,17 +4189,21 @@ pub const LiteralPool = struct { lp.data.deinit(allocator); } - pub fn getAtom(lp: LiteralPool, index: Index, macho_file: *MachO) *Atom { - assert(index < lp.values.items.len); - return macho_file.getAtom(lp.values.items[index]).?; - } - const InsertResult = struct { found_existing: bool, index: Index, - atom: *Atom.Index, + ref: *MachO.Ref, }; + pub fn getSymbolRef(lp: LiteralPool, index: Index) MachO.Ref { + assert(index < lp.values.items.len); + return lp.values.items[index]; + } + + pub fn getSymbol(lp: LiteralPool, index: Index, macho_file: *MachO) *Symbol { + return lp.getSymbolRef(index).getSymbol(macho_file).?; + } + pub fn insert(lp: *LiteralPool, allocator: Allocator, @"type": u8, string: []const u8) !InsertResult { const size: u32 = @intCast(string.len); try lp.data.ensureUnusedCapacity(allocator, size); @@ -4278,12 +4265,6 @@ const HotUpdateState = struct { mach_task: ?std.c.MachTask = null, }; -pub const DynamicRelocs = struct { - rebase_relocs: u32 = 0, - bind_relocs: u32 = 0, - weak_bind_relocs: u32 = 0, -}; - pub const SymtabCtx = struct { ilocal: u32 = 0, istab: u32 = 0, @@ -4293,6 +4274,7 @@ pub const SymtabCtx = struct { nstabs: u32 = 0, nexports: u32 = 0, nimports: u32 = 0, + stroff: u32 = 0, strsize: u32 = 0, }; @@ -4579,6 +4561,131 @@ const UndefinedTreatment = enum { dynamic_lookup, }; +/// A reference to atom or symbol in an input file. +/// If file == 0, symbol is an undefined global. +pub const Ref = struct { + index: u32, + file: File.Index, + + pub fn eql(ref: Ref, other: Ref) bool { + return ref.index == other.index and ref.file == other.file; + } + + pub fn getFile(ref: Ref, macho_file: *MachO) ?File { + return macho_file.getFile(ref.file); + } + + pub fn getAtom(ref: Ref, macho_file: *MachO) ?*Atom { + const file = ref.getFile(macho_file) orelse return null; + return file.getAtom(ref.index); + } + + pub fn getSymbol(ref: Ref, macho_file: *MachO) ?*Symbol { + const file = ref.getFile(macho_file) orelse return null; + return switch (file) { + inline else => |x| &x.symbols.items[ref.index], + }; + } + + pub fn format( + ref: Ref, + comptime unused_fmt_string: []const u8, + options: std.fmt.FormatOptions, + writer: anytype, + ) !void { + _ = unused_fmt_string; + _ = options; + try writer.print("%{d} in file({d})", .{ ref.index, ref.file }); + } +}; + +pub const SymbolResolver = struct { + keys: std.ArrayListUnmanaged(Key) = .{}, + values: std.ArrayListUnmanaged(Ref) = .{}, + table: std.AutoArrayHashMapUnmanaged(void, void) = .{}, + + const Result = struct { + found_existing: bool, + index: Index, + ref: *Ref, + }; + + pub fn deinit(resolver: *SymbolResolver, allocator: Allocator) void { + resolver.keys.deinit(allocator); + resolver.values.deinit(allocator); + resolver.table.deinit(allocator); + } + + pub fn getOrPut( + resolver: *SymbolResolver, + allocator: Allocator, + ref: Ref, + macho_file: *MachO, + ) !Result { + const adapter = Adapter{ .keys = resolver.keys.items, .macho_file = macho_file }; + const key = Key{ .index = ref.index, .file = ref.file }; + const gop = try resolver.table.getOrPutAdapted(allocator, key, adapter); + if (!gop.found_existing) { + try resolver.keys.append(allocator, key); + _ = try resolver.values.addOne(allocator); + } + return .{ + .found_existing = gop.found_existing, + .index = @intCast(gop.index + 1), + .ref = &resolver.values.items[gop.index], + }; + } + + pub fn get(resolver: SymbolResolver, index: Index) ?Ref { + if (index == 0) return null; + return resolver.values.items[index - 1]; + } + + pub fn reset(resolver: *SymbolResolver) void { + resolver.keys.clearRetainingCapacity(); + resolver.values.clearRetainingCapacity(); + resolver.table.clearRetainingCapacity(); + } + + const Key = struct { + index: Symbol.Index, + file: File.Index, + + fn getName(key: Key, macho_file: *MachO) [:0]const u8 { + const ref = Ref{ .index = key.index, .file = key.file }; + return ref.getSymbol(macho_file).?.getName(macho_file); + } + + fn eql(key: Key, other: Key, macho_file: *MachO) bool { + const key_name = key.getName(macho_file); + const other_name = other.getName(macho_file); + return mem.eql(u8, key_name, other_name); + } + + fn hash(key: Key, macho_file: *MachO) u32 { + const name = key.getName(macho_file); + return @truncate(Hash.hash(0, name)); + } + }; + + const Adapter = struct { + keys: []const Key, + macho_file: *MachO, + + pub fn eql(ctx: @This(), key: Key, b_void: void, b_map_index: usize) bool { + _ = b_void; + const other = ctx.keys[b_map_index]; + return key.eql(other, ctx.macho_file); + } + + pub fn hash(ctx: @This(), key: Key) u32 { + return key.hash(ctx.macho_file); + } + }; + + pub const Index = u32; +}; + const MachO = @This(); const std = @import("std"); diff --git a/src/link/MachO/Atom.zig b/src/link/MachO/Atom.zig index c7a65843bb..2ed86ef66f 100644 --- a/src/link/MachO/Atom.zig +++ b/src/link/MachO/Atom.zig @@ -47,16 +47,6 @@ pub fn getFile(self: Atom, macho_file: *MachO) File { return macho_file.getFile(self.file).?; } -pub fn getData(self: Atom, macho_file: *MachO, buffer: []u8) !void { - assert(buffer.len == self.size); - switch (self.getFile(macho_file)) { - .internal => |x| try x.getAtomData(self, buffer), - .object => |x| try x.getAtomData(macho_file, self, buffer), - .zig_object => |x| try x.getAtomData(macho_file, self, buffer), - else => unreachable, - } -} - pub fn getRelocs(self: Atom, macho_file: *MachO) []const Relocation { return switch (self.getFile(macho_file)) { .dylib => unreachable, @@ -88,8 +78,7 @@ pub fn getPriority(self: Atom, macho_file: *MachO) u64 { } pub fn getUnwindRecords(self: Atom, macho_file: *MachO) []const UnwindInfo.Record.Index { - if (!self.flags.unwind) return &[0]UnwindInfo.Record.Index{}; - const extra = self.getExtra(macho_file).?; + const extra = self.getExtra(macho_file); return switch (self.getFile(macho_file)) { .dylib => unreachable, .zig_object, .internal => &[0]UnwindInfo.Record.Index{}, @@ -110,44 +99,39 @@ pub fn markUnwindRecordsDead(self: Atom, macho_file: *MachO) void { } pub fn getThunk(self: Atom, macho_file: *MachO) *Thunk { - assert(self.flags.thunk); - const extra = self.getExtra(macho_file).?; + const extra = self.getExtra(macho_file); return macho_file.getThunk(extra.thunk); } -pub fn getLiteralPoolIndex(self: Atom, macho_file: *MachO) ?MachO.LiteralPool.Index { - if (!self.flags.literal_pool) return null; - return self.getExtra(macho_file).?.literal_index; -} - const AddExtraOpts = struct { thunk: ?u32 = null, rel_index: ?u32 = null, rel_count: ?u32 = null, + rel_out_index: ?u32 = null, + rel_out_count: ?u32 = null, unwind_index: ?u32 = null, unwind_count: ?u32 = null, - literal_index: ?u32 = null, + literal_pool_index: ?u32 = null, + literal_symbol_index: ?u32 = null, }; -pub fn addExtra(atom: *Atom, opts: AddExtraOpts, macho_file: *MachO) !void { - if (atom.getExtra(macho_file) == null) { - atom.extra = try macho_file.addAtomExtra(.{}); - } - var extra = atom.getExtra(macho_file).?; +pub fn addExtra(atom: *Atom, opts: AddExtraOpts, macho_file: *MachO) void { + const file = atom.getFile(macho_file); + var extra = file.getAtomExtra(atom.extra); inline for (@typeInfo(@TypeOf(opts)).Struct.fields) |field| { if (@field(opts, field.name)) |x| { @field(extra, field.name) = x; } } - atom.setExtra(extra, macho_file); + file.setAtomExtra(atom.extra, extra); } -pub inline fn getExtra(atom: Atom, macho_file: *MachO) ?Extra { - return macho_file.getAtomExtra(atom.extra); +pub inline fn getExtra(atom: Atom, macho_file: *MachO) Extra { + return atom.getFile(macho_file).getAtomExtra(atom.extra); } pub inline fn setExtra(atom: Atom, extra: Extra, macho_file: *MachO) void { - macho_file.setAtomExtra(atom.extra, extra); + atom.getFile(macho_file).setAtomExtra(atom.extra, extra); } pub fn initOutputSection(sect: macho.section_64, macho_file: *MachO) !u8 { @@ -467,7 +451,7 @@ pub fn scanRelocs(self: Atom, macho_file: *MachO) !void { switch (rel.type) { .branch => { - const symbol = rel.getTargetSymbol(macho_file); + const symbol = rel.getTargetSymbol(self, macho_file); if (symbol.flags.import or (symbol.flags.@"export" and symbol.flags.weak) or symbol.flags.interposable) { symbol.flags.stubs = true; if (symbol.flags.weak) { @@ -482,7 +466,7 @@ pub fn scanRelocs(self: Atom, macho_file: *MachO) !void { .got_load_page, .got_load_pageoff, => { - const symbol = rel.getTargetSymbol(macho_file); + const symbol = rel.getTargetSymbol(self, macho_file); if (symbol.flags.import or (symbol.flags.@"export" and symbol.flags.weak) or symbol.flags.interposable or @@ -496,18 +480,18 @@ pub fn scanRelocs(self: Atom, macho_file: *MachO) !void { }, .zig_got_load => { - assert(rel.getTargetSymbol(macho_file).flags.has_zig_got); + assert(rel.getTargetSymbol(self, macho_file).flags.has_zig_got); }, .got => { - rel.getTargetSymbol(macho_file).flags.needs_got = true; + rel.getTargetSymbol(self, macho_file).flags.needs_got = true; }, .tlv, .tlvp_page, .tlvp_pageoff, => { - const symbol = rel.getTargetSymbol(macho_file); + const symbol = rel.getTargetSymbol(self, macho_file); if (!symbol.flags.tlv) { try macho_file.reportParseError2( self.getFile(macho_file).getIndex(), @@ -526,7 +510,7 @@ pub fn scanRelocs(self: Atom, macho_file: *MachO) !void { .unsigned => { if (rel.meta.length == 3) { // TODO this really should check if this is pointer width if (rel.tag == .@"extern") { - const symbol = rel.getTargetSymbol(macho_file); + const symbol = rel.getTargetSymbol(self, macho_file); if (symbol.isTlvInit(macho_file)) { macho_file.has_tlv = true; continue; @@ -559,14 +543,15 @@ pub fn scanRelocs(self: Atom, macho_file: *MachO) !void { fn reportUndefSymbol(self: Atom, rel: Relocation, macho_file: *MachO) !bool { if (rel.tag == .local) return false; - const sym = rel.getTargetSymbol(macho_file); - if (sym.getFile(macho_file) == null) { + const file = self.getFile(macho_file); + const ref = file.getSymbolRef(rel.target, macho_file); + if (ref.getFile(macho_file) == null) { const gpa = macho_file.base.comp.gpa; - const gop = try macho_file.undefs.getOrPut(gpa, rel.target); + const gop = try macho_file.undefs.getOrPut(gpa, .{ .index = rel.target, .file = self.file }); if (!gop.found_existing) { gop.value_ptr.* = .{}; } - try gop.value_ptr.append(gpa, self.atom_index); + try gop.value_ptr.append(gpa, .{ .index = self.atom_index, .file = self.file }); return true; } @@ -582,7 +567,7 @@ pub fn resolveRelocs(self: Atom, macho_file: *MachO, buffer: []u8) !void { const name = self.getName(macho_file); const relocs = self.getRelocs(macho_file); - relocs_log.debug("{x}: {s}", .{ self.getAddress(macho_file), name }); + relocs_log.debug("{x}: {s}", .{ self.value, name }); var has_error = false; var stream = std.io.fixedBufferStream(buffer); @@ -593,7 +578,7 @@ pub fn resolveRelocs(self: Atom, macho_file: *MachO, buffer: []u8) !void { const subtractor = if (rel.meta.has_subtractor) relocs[i - 1] else null; if (rel.tag == .@"extern") { - if (rel.getTargetSymbol(macho_file).getFile(macho_file) == null) continue; + if (rel.getTargetSymbol(self, macho_file).getFile(macho_file) == null) continue; } try stream.seekTo(rel_offset); @@ -601,8 +586,8 @@ pub fn resolveRelocs(self: Atom, macho_file: *MachO, buffer: []u8) !void { switch (err) { error.RelaxFail => { const target = switch (rel.tag) { - .@"extern" => rel.getTargetSymbol(macho_file).getName(macho_file), - .local => rel.getTargetAtom(macho_file).getName(macho_file), + .@"extern" => rel.getTargetSymbol(self, macho_file).getName(macho_file), + .local => rel.getTargetAtom(self, macho_file).getName(macho_file), }; try macho_file.reportParseError2( file.getIndex(), @@ -642,12 +627,12 @@ fn resolveRelocInner( const rel_offset = math.cast(usize, rel.offset - self.off) orelse return error.Overflow; const P = @as(i64, @intCast(self.getAddress(macho_file))) + @as(i64, @intCast(rel_offset)); const A = rel.addend + rel.getRelocAddend(cpu_arch); - const S: i64 = @intCast(rel.getTargetAddress(macho_file)); - const G: i64 = @intCast(rel.getGotTargetAddress(macho_file)); + const S: i64 = @intCast(rel.getTargetAddress(self, macho_file)); + const G: i64 = @intCast(rel.getGotTargetAddress(self, macho_file)); const TLS = @as(i64, @intCast(macho_file.getTlsAddress())); - const SUB = if (subtractor) |sub| @as(i64, @intCast(sub.getTargetAddress(macho_file))) else 0; + const SUB = if (subtractor) |sub| @as(i64, @intCast(sub.getTargetAddress(self, macho_file))) else 0; // Address of the __got_zig table entry if any. - const ZIG_GOT = @as(i64, @intCast(rel.getZigGotTargetAddress(macho_file))); + const ZIG_GOT = @as(i64, @intCast(rel.getZigGotTargetAddress(self, macho_file))); const divExact = struct { fn divExact(atom: Atom, r: Relocation, num: u12, den: u12, ctx: *MachO) !u12 { @@ -668,7 +653,7 @@ fn resolveRelocInner( rel_offset, @tagName(rel.type), S + A - SUB, - rel.getTargetAtom(macho_file).atom_index, + rel.getTargetAtom(self, macho_file).atom_index, }), .@"extern" => relocs_log.debug(" {x}<+{d}>: {s}: [=> {x}] G({x}) ZG({x}) ({s})", .{ P, @@ -677,7 +662,7 @@ fn resolveRelocInner( S + A - SUB, G + A, ZIG_GOT + A, - rel.getTargetSymbol(macho_file).getName(macho_file), + rel.getTargetSymbol(self, macho_file).getName(macho_file), }), } @@ -688,7 +673,7 @@ fn resolveRelocInner( assert(!rel.meta.pcrel); if (rel.meta.length == 3) { if (rel.tag == .@"extern") { - const sym = rel.getTargetSymbol(macho_file); + const sym = rel.getTargetSymbol(self, macho_file); if (sym.isTlvInit(macho_file)) { try writer.writeInt(u64, @intCast(S - TLS), .little); return; @@ -718,7 +703,7 @@ fn resolveRelocInner( .aarch64 => { const disp: i28 = math.cast(i28, S + A - P) orelse blk: { const thunk = self.getThunk(macho_file); - const S_: i64 = @intCast(thunk.getTargetAddress(rel.target, macho_file)); + const S_: i64 = @intCast(thunk.getTargetAddress(rel.getTargetSymbolRef(self, macho_file), macho_file)); break :blk math.cast(i28, S_ + A - P) orelse return error.Overflow; }; aarch64.writeBranchImm(disp, code[rel_offset..][0..4]); @@ -731,7 +716,7 @@ fn resolveRelocInner( assert(rel.tag == .@"extern"); assert(rel.meta.length == 2); assert(rel.meta.pcrel); - if (rel.getTargetSymbol(macho_file).flags.has_got) { + if (rel.getTargetSymbol(self, macho_file).flags.has_got) { try writer.writeInt(i32, @intCast(G + A - P), .little); } else { try x86_64.relaxGotLoad(self, code[rel_offset - 3 ..], rel, macho_file); @@ -754,7 +739,7 @@ fn resolveRelocInner( assert(rel.tag == .@"extern"); assert(rel.meta.length == 2); assert(rel.meta.pcrel); - const sym = rel.getTargetSymbol(macho_file); + const sym = rel.getTargetSymbol(self, macho_file); if (sym.flags.tlv_ptr) { const S_: i64 = @intCast(sym.getTlvPtrAddress(macho_file)); try writer.writeInt(i32, @intCast(S_ + A - P), .little); @@ -777,7 +762,7 @@ fn resolveRelocInner( assert(rel.tag == .@"extern"); assert(rel.meta.length == 2); assert(rel.meta.pcrel); - const sym = rel.getTargetSymbol(macho_file); + const sym = rel.getTargetSymbol(self, macho_file); const source = math.cast(u64, P) orelse return error.Overflow; const target = target: { const target = switch (rel.type) { @@ -836,7 +821,7 @@ fn resolveRelocInner( assert(rel.meta.length == 2); assert(!rel.meta.pcrel); - const sym = rel.getTargetSymbol(macho_file); + const sym = rel.getTargetSymbol(self, macho_file); const target = target: { const target = if (sym.flags.tlv_ptr) blk: { const S_: i64 = @intCast(sym.getTlvPtrAddress(macho_file)); @@ -980,48 +965,47 @@ pub fn calcNumRelocs(self: Atom, macho_file: *MachO) u32 { } } -pub fn writeRelocs(self: Atom, macho_file: *MachO, code: []u8, buffer: *std.ArrayList(macho.relocation_info)) !void { +pub fn writeRelocs(self: Atom, macho_file: *MachO, code: []u8, buffer: []macho.relocation_info) !void { const tracy = trace(@src()); defer tracy.end(); const cpu_arch = macho_file.getTarget().cpu.arch; const relocs = self.getRelocs(macho_file); - var stream = std.io.fixedBufferStream(code); + var i: usize = 0; for (relocs) |rel| { + defer i += 1; const rel_offset = rel.offset - self.off; const r_address: i32 = math.cast(i32, self.value + rel_offset) orelse return error.Overflow; const r_symbolnum = r_symbolnum: { const r_symbolnum: u32 = switch (rel.tag) { - .local => rel.getTargetAtom(macho_file).out_n_sect + 1, - .@"extern" => rel.getTargetSymbol(macho_file).getOutputSymtabIndex(macho_file).?, + .local => rel.getTargetAtom(self, macho_file).out_n_sect + 1, + .@"extern" => rel.getTargetSymbol(self, macho_file).getOutputSymtabIndex(macho_file).?, }; break :r_symbolnum math.cast(u24, r_symbolnum) orelse return error.Overflow; }; const r_extern = rel.tag == .@"extern"; var addend = rel.addend + rel.getRelocAddend(cpu_arch); if (rel.tag == .local) { - const target: i64 = @intCast(rel.getTargetAddress(macho_file)); + const target: i64 = @intCast(rel.getTargetAddress(self, macho_file)); addend += target; } - try stream.seekTo(rel_offset); - switch (cpu_arch) { .aarch64 => { if (rel.type == .unsigned) switch (rel.meta.length) { 0, 1 => unreachable, - 2 => try stream.writer().writeInt(i32, @truncate(addend), .little), - 3 => try stream.writer().writeInt(i64, addend, .little), + 2 => mem.writeInt(i32, code[rel_offset..][0..4], @truncate(addend), .little), + 3 => mem.writeInt(i64, code[rel_offset..][0..8], addend, .little), } else if (addend > 0) { - buffer.appendAssumeCapacity(.{ + buffer[i] = .{ .r_address = r_address, .r_symbolnum = @bitCast(math.cast(i24, addend) orelse return error.Overflow), .r_pcrel = 0, .r_length = 2, .r_extern = 0, .r_type = @intFromEnum(macho.reloc_type_arm64.ARM64_RELOC_ADDEND), - }); + }; } 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 .tlv, => unreachable, }; - buffer.appendAssumeCapacity(.{ + buffer[i] = .{ .r_address = r_address, .r_symbolnum = r_symbolnum, .r_pcrel = @intFromBool(rel.meta.pcrel), .r_extern = @intFromBool(r_extern), .r_length = rel.meta.length, .r_type = @intFromEnum(r_type), - }); + }; }, .x86_64 => { if (rel.meta.pcrel) { @@ -1064,8 +1048,8 @@ pub fn writeRelocs(self: Atom, macho_file: *MachO, code: []u8, buffer: *std.Arra } switch (rel.meta.length) { 0, 1 => unreachable, - 2 => try stream.writer().writeInt(i32, @truncate(addend), .little), - 3 => try stream.writer().writeInt(i64, addend, .little), + 2 => mem.writeInt(i32, code[rel_offset..][0..4], @truncate(addend), .little), + 3 => mem.writeInt(i64, code[rel_offset..][0..8], addend, .little), } 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 .tlvp_pageoff, => unreachable, }; - buffer.appendAssumeCapacity(.{ + buffer[i] = .{ .r_address = r_address, .r_symbolnum = r_symbolnum, .r_pcrel = @intFromBool(rel.meta.pcrel), .r_extern = @intFromBool(r_extern), .r_length = rel.meta.length, .r_type = @intFromEnum(r_type), - }); + }; }, else => unreachable, } } + + assert(i == buffer.len); } pub fn format( @@ -1139,16 +1125,15 @@ fn format2( const atom = ctx.atom; const macho_file = ctx.macho_file; const file = atom.getFile(macho_file); - try writer.print("atom({d}) : {s} : @{x} : sect({d}) : align({x}) : size({x}) : nreloc({d})", .{ - atom.atom_index, atom.getName(macho_file), atom.getAddress(macho_file), - atom.out_n_sect, atom.alignment, atom.size, - atom.getRelocs(macho_file).len, + try writer.print("atom({d}) : {s} : @{x} : sect({d}) : align({x}) : size({x}) : nreloc({d}) : thunk({d})", .{ + atom.atom_index, atom.getName(macho_file), atom.getAddress(macho_file), + atom.out_n_sect, atom.alignment, atom.size, + atom.getRelocs(macho_file).len, atom.getExtra(macho_file).thunk, }); - if (atom.flags.thunk) try writer.print(" : thunk({d})", .{atom.getExtra(macho_file).?.thunk}); if (!atom.flags.alive) try writer.writeAll(" : [*]"); - if (atom.flags.unwind) { + if (atom.getUnwindRecords(macho_file).len > 0) { try writer.writeAll(" : unwind{ "); - const extra = atom.getExtra(macho_file).?; + const extra = atom.getExtra(macho_file); for (atom.getUnwindRecords(macho_file), extra.unwind_index..) |index, i| { const rec = file.object.getUnwindRecord(index); try writer.print("{d}", .{index}); @@ -1167,18 +1152,6 @@ pub const Flags = packed struct { /// Specifies if this atom has been visited during garbage collection. visited: bool = false, - - /// Whether this atom has a range extension thunk. - thunk: bool = false, - - /// Whether this atom has any relocations. - relocs: bool = false, - - /// Whether this atom has any unwind records. - unwind: bool = false, - - /// Whether this atom has LiteralPool entry. - literal_pool: bool = false, }; pub const Extra = struct { @@ -1191,6 +1164,12 @@ pub const Extra = struct { /// Count of relocations belonging to this atom. rel_count: u32 = 0, + /// Start index of relocations being written out to file for this atom. + rel_out_index: u32 = 0, + + /// Count of relocations written out to file for this atom. + rel_out_count: u32 = 0, + /// Start index of relocations belonging to this atom. unwind_index: u32 = 0, @@ -1198,7 +1177,10 @@ pub const Extra = struct { unwind_count: u32 = 0, /// Index into LiteralPool entry for this atom. - literal_index: u32 = 0, + literal_pool_index: u32 = 0, + + /// Index into the File's symbol table for local symbol representing this literal atom. + literal_symbol_index: u32 = 0, }; pub const Alignment = @import("../../InternPool.zig").Alignment; diff --git a/src/link/MachO/InternalObject.zig b/src/link/MachO/InternalObject.zig index 2499f01a03..32e89116b1 100644 --- a/src/link/MachO/InternalObject.zig +++ b/src/link/MachO/InternalObject.zig @@ -639,6 +639,12 @@ pub fn asFile(self: *InternalObject) File { return .{ .internal = self }; } +pub fn getAtomRelocs(self: *const InternalObject, atom: Atom, macho_file: *MachO) []const Relocation { + const extra = atom.getExtra(macho_file).?; + const relocs = self.sections.items(.relocs)[atom.n_sect]; + return relocs.items[extra.rel_index..][0..extra.rel_count]; +} + fn addAtom(self: *InternalObject, allocator: Allocator) !Atom.Index { const atom_index: Atom.Index = @intCast(self.atoms.items.len); const atom = try self.atoms.addOne(allocator); diff --git a/src/link/MachO/Object.zig b/src/link/MachO/Object.zig index fcbb31f4ed..17dd198c6b 100644 --- a/src/link/MachO/Object.zig +++ b/src/link/MachO/Object.zig @@ -1860,7 +1860,7 @@ pub fn calcStabsSize(self: *Object, macho_file: *MachO) error{Overflow}!void { const name = sym.getName(macho_file); if (name.len > 0 and (name[0] == 'L' or name[0] == 'l')) continue; } - const sect = macho_file.sections.items(.header)[sym.out_n_sect]; + const sect = macho_file.sections.items(.header)[sym.getOutputSectionIndex(macho_file)]; if (sect.isCode()) { self.output_symtab_ctx.nstabs += 4; // N_BNSYM, N_FUN, N_FUN, N_ENSYM } else if (sym.visibility == .global) { @@ -2198,13 +2198,13 @@ pub fn writeStabs(self: *const Object, stroff: u32, macho_file: *MachO) void { const name = sym.getName(macho_file); if (name.len > 0 and (name[0] == 'L' or name[0] == 'l')) continue; } - const sect = macho_file.sections.items(.header)[sym.out_n_sect]; + const sect = macho_file.sections.items(.header)[sym.getOutputSectionIndex(macho_file)]; const sym_n_strx = n_strx: { const symtab_index = sym.getOutputSymtabIndex(macho_file).?; const osym = macho_file.symtab.items[symtab_index]; break :n_strx osym.n_strx; }; - const sym_n_sect: u8 = if (!sym.flags.abs) @intCast(sym.out_n_sect + 1) else 0; + const sym_n_sect: u8 = if (!sym.flags.abs) @intCast(sym.getOutputSectionIndex(macho_file) + 1) else 0; const sym_n_value = sym.getAddress(.{}, macho_file); const sym_size = sym.getSize(macho_file); if (sect.isCode()) { @@ -2299,7 +2299,7 @@ pub fn writeStabs(self: *const Object, stroff: u32, macho_file: *MachO) void { const osym = macho_file.symtab.items[symtab_index]; break :n_strx osym.n_strx; }; - const sym_n_sect: u8 = if (!sym.flags.abs) @intCast(sym.out_n_sect + 1) else 0; + const sym_n_sect: u8 = if (!sym.flags.abs) @intCast(sym.getOutputSectionIndex(macho_file) + 1) else 0; const sym_n_value = sym.getAddress(.{}, macho_file); const sym_size = sym.getSize(macho_file); if (stab.is_func) { @@ -2340,6 +2340,12 @@ pub fn writeStabs(self: *const Object, stroff: u32, macho_file: *MachO) void { } } +pub fn getAtomRelocs(self: *const Object, atom: Atom, macho_file: *MachO) []const Relocation { + const extra = atom.getExtra(macho_file).?; + const relocs = self.sections.items(.relocs)[atom.n_sect]; + return relocs.items[extra.rel_index..][0..extra.rel_count]; +} + fn addString(self: *Object, allocator: Allocator, name: [:0]const u8) error{OutOfMemory}!u32 { const off: u32 = @intCast(self.strtab.items.len); try self.strtab.ensureUnusedCapacity(allocator, name.len + 1); diff --git a/src/link/MachO/Symbol.zig b/src/link/MachO/Symbol.zig index e413fe818d..003996fc95 100644 --- a/src/link/MachO/Symbol.zig +++ b/src/link/MachO/Symbol.zig @@ -9,17 +9,16 @@ name: u32 = 0, /// File where this symbol is defined. file: File.Index = 0, -/// Atom containing this symbol if any. -/// Index of 0 means there is no associated atom with this symbol. +/// Reference to Atom containing this symbol if any. /// Use `getAtom` to get the pointer to the atom. -atom: Atom.Index = 0, +atom_ref: MachO.Ref = .{ .index = 0, .file = 0 }, /// Assigned output section index for this symbol. out_n_sect: u8 = 0, /// Index of the source nlist this symbol references. /// Use `getNlist` to pull the nlist from the relevant file. -nlist_idx: Index = 0, +nlist_idx: u32 = 0, /// Misc flags for the symbol packaged as packed struct for compression. flags: Flags = .{}, @@ -55,16 +54,19 @@ pub fn weakRef(symbol: Symbol, macho_file: *MachO) bool { } pub fn getName(symbol: Symbol, macho_file: *MachO) [:0]const u8 { - if (symbol.flags.global) return macho_file.strings.getAssumeExists(symbol.name); return switch (symbol.getFile(macho_file).?) { - .dylib => unreachable, // There are no local symbols for dylibs .zig_object => |x| x.strtab.getAssumeExists(symbol.name), inline else => |x| x.getString(symbol.name), }; } pub fn getAtom(symbol: Symbol, macho_file: *MachO) ?*Atom { - return macho_file.getAtom(symbol.atom); + return symbol.atom_ref.getAtom(macho_file); +} + +pub fn getOutputSectionIndex(symbol: Symbol, macho_file: *MachO) u8 { + if (symbol.getAtom(macho_file)) |atom| return atom.out_n_sect; + return symbol.out_n_sect; } pub fn getFile(symbol: Symbol, macho_file: *MachO) ?File { @@ -75,8 +77,10 @@ pub fn getFile(symbol: Symbol, macho_file: *MachO) ?File { pub fn getNlist(symbol: Symbol, macho_file: *MachO) macho.nlist_64 { const file = symbol.getFile(macho_file).?; return switch (file) { + .dylib => unreachable, + .zig_object => unreachable, .object => |x| x.symtab.items(.nlist)[symbol.nlist_idx], - else => unreachable, + .internal => |x| x.symtab.items[symbol.nlist_idx], }; } @@ -124,33 +128,35 @@ pub fn getAddress(symbol: Symbol, opts: struct { pub fn getGotAddress(symbol: Symbol, macho_file: *MachO) u64 { if (!symbol.flags.has_got) return 0; - const extra = symbol.getExtra(macho_file).?; + const extra = symbol.getExtra(macho_file); return macho_file.got.getAddress(extra.got, macho_file); } pub fn getStubsAddress(symbol: Symbol, macho_file: *MachO) u64 { if (!symbol.flags.stubs) return 0; - const extra = symbol.getExtra(macho_file).?; + const extra = symbol.getExtra(macho_file); return macho_file.stubs.getAddress(extra.stubs, macho_file); } pub fn getObjcStubsAddress(symbol: Symbol, macho_file: *MachO) u64 { if (!symbol.flags.objc_stubs) return 0; - const extra = symbol.getExtra(macho_file).?; + const extra = symbol.getExtra(macho_file); return macho_file.objc_stubs.getAddress(extra.objc_stubs, macho_file); } pub fn getObjcSelrefsAddress(symbol: Symbol, macho_file: *MachO) u64 { if (!symbol.flags.objc_stubs) return 0; - const extra = symbol.getExtra(macho_file).?; - const atom = macho_file.getAtom(extra.objc_selrefs).?; - assert(atom.flags.alive); - return atom.getAddress(macho_file); + const extra = symbol.getExtra(macho_file); + const file = symbol.getFile(macho_file).?; + return switch (file) { + .dylib, .zig_object => unreachable, + .object, .internal => |x| x.symbols.items[extra.objc_selrefs].getAddress(.{}, macho_file), + }; } pub fn getTlvPtrAddress(symbol: Symbol, macho_file: *MachO) u64 { if (!symbol.flags.tlv_ptr) return 0; - const extra = symbol.getExtra(macho_file).?; + const extra = symbol.getExtra(macho_file); return macho_file.tlv_ptr.getAddress(extra.tlv_ptr, macho_file); } @@ -162,14 +168,14 @@ const GetOrCreateZigGotEntryResult = struct { pub fn getOrCreateZigGotEntry(symbol: *Symbol, symbol_index: Index, macho_file: *MachO) !GetOrCreateZigGotEntryResult { assert(!macho_file.base.isRelocatable()); assert(symbol.flags.needs_zig_got); - if (symbol.flags.has_zig_got) return .{ .found_existing = true, .index = symbol.getExtra(macho_file).?.zig_got }; + if (symbol.flags.has_zig_got) return .{ .found_existing = true, .index = symbol.getExtra(macho_file).zig_got }; const index = try macho_file.zig_got.addSymbol(symbol_index, macho_file); return .{ .found_existing = false, .index = index }; } pub fn getZigGotAddress(symbol: Symbol, macho_file: *MachO) u64 { if (!symbol.flags.has_zig_got) return 0; - const extras = symbol.getExtra(macho_file).?; + const extras = symbol.getExtra(macho_file); return macho_file.zig_got.entryAddress(extras.zig_got, macho_file); } @@ -202,11 +208,8 @@ const AddExtraOpts = struct { symtab: ?u32 = null, }; -pub fn addExtra(symbol: *Symbol, opts: AddExtraOpts, macho_file: *MachO) !void { - if (symbol.getExtra(macho_file) == null) { - symbol.extra = try macho_file.addSymbolExtra(.{}); - } - var extra = symbol.getExtra(macho_file).?; +pub fn addExtra(symbol: *Symbol, opts: AddExtraOpts, macho_file: *MachO) void { + var extra = symbol.getExtra(macho_file); inline for (@typeInfo(@TypeOf(opts)).Struct.fields) |field| { if (@field(opts, field.name)) |x| { @field(extra, field.name) = x; @@ -215,18 +218,22 @@ pub fn addExtra(symbol: *Symbol, opts: AddExtraOpts, macho_file: *MachO) !void { symbol.setExtra(extra, macho_file); } -pub inline fn getExtra(symbol: Symbol, macho_file: *MachO) ?Extra { - return macho_file.getSymbolExtra(symbol.extra); +pub inline fn getExtra(symbol: Symbol, macho_file: *MachO) Extra { + return switch (symbol.getFile(macho_file).?) { + inline else => |x| x.getSymbolExtra(symbol.extra), + }; } pub inline fn setExtra(symbol: Symbol, extra: Extra, macho_file: *MachO) void { - macho_file.setSymbolExtra(symbol.extra, extra); + return switch (symbol.getFile(macho_file).?) { + inline else => |x| x.setSymbolExtra(symbol.extra, extra), + }; } pub fn setOutputSym(symbol: Symbol, macho_file: *MachO, out: *macho.nlist_64) void { if (symbol.isLocal()) { out.n_type = if (symbol.flags.abs) macho.N_ABS else macho.N_SECT; - out.n_sect = if (symbol.flags.abs) 0 else @intCast(symbol.out_n_sect + 1); + out.n_sect = if (symbol.flags.abs) 0 else @intCast(symbol.getOutputSectionIndex(macho_file) + 1); out.n_desc = 0; out.n_value = symbol.getAddress(.{ .stubs = false }, macho_file); @@ -238,7 +245,7 @@ pub fn setOutputSym(symbol: Symbol, macho_file: *MachO, out: *macho.nlist_64) vo assert(symbol.visibility == .global); out.n_type = macho.N_EXT; out.n_type |= if (symbol.flags.abs) macho.N_ABS else macho.N_SECT; - out.n_sect = if (symbol.flags.abs) 0 else @intCast(symbol.out_n_sect + 1); + out.n_sect = if (symbol.flags.abs) 0 else @intCast(symbol.getOutputSectionIndex(macho_file) + 1); out.n_value = symbol.getAddress(.{ .stubs = false }, macho_file); out.n_desc = 0; @@ -318,8 +325,8 @@ fn format2( symbol.getAddress(.{}, ctx.macho_file), }); if (symbol.getFile(ctx.macho_file)) |file| { - if (symbol.out_n_sect != 0) { - try writer.print(" : sect({d})", .{symbol.out_n_sect}); + if (symbol.getOutputSectionIndex(ctx.macho_file) != 0) { + try writer.print(" : sect({d})", .{symbol.getOutputSectionIndex(ctx.macho_file)}); } if (symbol.getAtom(ctx.macho_file)) |atom| { try writer.print(" : atom({d})", .{atom.atom_index}); @@ -346,11 +353,6 @@ pub const Flags = packed struct { /// Whether the symbol is exported at runtime. @"export": bool = false, - /// Whether the symbol is effectively an extern and takes part in global - /// symbol resolution. Then, its name will be saved in global string interning - /// table. - global: bool = false, - /// Whether this symbol is weak. weak: bool = false,