From 711bc2cf3919a2010503610574e7dfcad8a17929 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Tue, 18 Apr 2023 17:12:05 +0200 Subject: [PATCH] macho: use generic TableSection for GOT mgmt --- src/link/MachO.zig | 125 ++++++++++++++++++++------------ src/link/MachO/DebugSymbols.zig | 4 +- src/link/MachO/Relocation.zig | 49 +++++++------ 3 files changed, 107 insertions(+), 71 deletions(-) diff --git a/src/link/MachO.zig b/src/link/MachO.zig index 0ffb72f087..0efbf3b270 100644 --- a/src/link/MachO.zig +++ b/src/link/MachO.zig @@ -41,6 +41,7 @@ const Md5 = std.crypto.hash.Md5; const Module = @import("../Module.zig"); const Relocation = @import("MachO/Relocation.zig"); const StringTable = @import("strtab.zig").StringTable; +const TableSection = @import("table_section.zig").TableSection; const Trie = @import("MachO/Trie.zig"); const Type = @import("../type.zig").Type; const TypedValue = @import("../TypedValue.zig"); @@ -154,13 +155,14 @@ stub_helper_preamble_atom_index: ?Atom.Index = null, strtab: StringTable(.strtab) = .{}, -got_table: SectionTable = .{}, +got_table: TableSection(SymbolWithLoc) = .{}, stubs_table: SectionTable = .{}, tlv_table: SectionTable = .{}, error_flags: File.ErrorFlags = File.ErrorFlags{}, segment_table_dirty: bool = false, +got_table_count_dirty: bool = false, /// A helper var to indicate if we are at the start of the incremental updates, or /// already somewhere further along the update-and-run chain. @@ -1270,6 +1272,30 @@ fn updateAtomInMemory(self: *MachO, task: std.os.darwin.MachTask, segment_index: if (nwritten != code.len) return error.InputOutput; } +fn writeOffsetTableEntry(self: *MachO, index: @TypeOf(self.got_table).Index) !void { + const sect_id = self.got_section_index.?; + + if (self.got_table_count_dirty) { + const needed_size = self.got_table.entries.items.len * @sizeOf(u64); + try self.growSection(sect_id, needed_size); + self.got_table_count_dirty = false; + } + + const header = &self.sections.items(.header)[sect_id]; + const entry = self.got_table.entries.items[index]; + const entry_value = self.getSymbol(entry).n_value; + const entry_offset = index * @sizeOf(u64); + const file_offset = header.offset + entry_offset; + const vmaddr = header.addr + entry_offset; + _ = vmaddr; + + var buf: [8]u8 = undefined; + mem.writeIntLittle(u64, &buf, entry_value); + try self.base.file.?.pwriteAll(&buf, file_offset); + + // TODO write in memory +} + fn writePtrWidthAtom(self: *MachO, atom_index: Atom.Index) !void { var buffer: [@sizeOf(u64)]u8 = [_]u8{0} ** @sizeOf(u64); try self.writeAtom(atom_index, &buffer); @@ -1290,10 +1316,9 @@ fn markRelocsDirtyByAddress(self: *MachO, addr: u64) void { log.debug("marking relocs dirty by address: {x}", .{addr}); for (self.relocs.values()) |*relocs| { for (relocs.items) |*reloc| { - const target_atom_index = reloc.getTargetAtomIndex(self) orelse continue; - const target_atom = self.getAtom(target_atom_index); - const target_sym = target_atom.getSymbol(self); - if (target_sym.n_value < addr) continue; + const target_addr = reloc.getTargetBaseAddress(self) orelse continue; + if (target_addr == 0) continue; + if (target_addr < addr) continue; reloc.dirty = true; } } @@ -1335,40 +1360,6 @@ pub fn createAtom(self: *MachO) !Atom.Index { return atom_index; } -pub fn createGotAtom(self: *MachO, target: SymbolWithLoc) !Atom.Index { - const atom_index = try self.createAtom(); - self.getAtomPtr(atom_index).size = @sizeOf(u64); - - const sym = self.getAtom(atom_index).getSymbolPtr(self); - sym.n_type = macho.N_SECT; - sym.n_sect = self.got_section_index.? + 1; - sym.n_value = try self.allocateAtom(atom_index, @sizeOf(u64), @alignOf(u64)); - - log.debug("allocated GOT atom at 0x{x}", .{sym.n_value}); - - try Atom.addRelocation(self, atom_index, .{ - .type = .unsigned, - .target = target, - .offset = 0, - .addend = 0, - .pcrel = false, - .length = 3, - }); - - const target_sym = self.getSymbol(target); - if (target_sym.undf()) { - try Atom.addBinding(self, atom_index, .{ - .target = self.getGlobal(self.getSymbolName(target)).?, - .offset = 0, - }); - } else { - try Atom.addRebase(self, atom_index, 0); - } - try self.writePtrWidthAtom(atom_index); - - return atom_index; -} - fn createDyldPrivateAtom(self: *MachO) !void { if (self.dyld_private_atom_index != null) return; @@ -2104,9 +2095,7 @@ fn allocateGlobal(self: *MachO) !u32 { fn addGotEntry(self: *MachO, target: SymbolWithLoc) !void { if (self.got_table.lookup.contains(target)) return; const got_index = try self.got_table.allocateEntry(self.base.allocator, target); - const got_atom_index = try self.createGotAtom(target); - const got_atom = self.getAtom(got_atom_index); - self.got_table.entries.items[got_index].sym_index = got_atom.getSymbolIndex().?; + try self.writeOffsetTableEntry(got_index); self.markRelocsDirtyByTarget(target); } @@ -2532,8 +2521,8 @@ fn updateDeclCode(self: *MachO, decl_index: Module.Decl.Index, code: []u8) !u64 } else .{ .sym_index = sym_index }; self.markRelocsDirtyByTarget(target); log.debug(" (updating GOT entry)", .{}); - const got_atom_index = self.got_table.getAtomIndex(self, target).?; - try self.writePtrWidthAtom(got_atom_index); + const got_atom_index = self.got_table.lookup.get(target).?; + try self.writeOffsetTableEntry(got_atom_index); } } else if (code_len < atom.size) { self.shrinkAtom(atom_index, code_len); @@ -3276,6 +3265,20 @@ fn collectRebaseData(self: *MachO, rebase: *Rebase) !void { } } + // Gather GOT pointers + const segment_index = self.sections.items(.segment_index)[self.got_section_index.?]; + for (self.got_table.entries.items, 0..) |entry, i| { + if (!self.got_table.lookup.contains(entry)) continue; + const sym = self.getSymbol(entry); + if (sym.undf()) continue; + const offset = i * @sizeOf(u64); + log.debug(" | rebase at {x}", .{offset}); + rebase.entries.appendAssumeCapacity(.{ + .offset = offset, + .segment_id = segment_index, + }); + } + try rebase.finalize(gpa); } @@ -3320,6 +3323,32 @@ fn collectBindData(self: *MachO, bind: anytype, raw_bindings: anytype) !void { } } + // Gather GOT pointers + const segment_index = self.sections.items(.segment_index)[self.got_section_index.?]; + for (self.got_table.entries.items, 0..) |entry, i| { + if (!self.got_table.lookup.contains(entry)) continue; + const sym = self.getSymbol(entry); + if (!sym.undf()) continue; + const offset = i * @sizeOf(u64); + const bind_sym = self.getSymbol(entry); + const bind_sym_name = self.getSymbolName(entry); + const dylib_ordinal = @divTrunc( + @bitCast(i16, bind_sym.n_desc), + macho.N_SYMBOL_RESOLVER, + ); + log.debug(" | bind at {x}, import('{s}') in dylib({d})", .{ + offset, + bind_sym_name, + dylib_ordinal, + }); + bind.entries.appendAssumeCapacity(.{ + .target = entry, + .offset = offset, + .segment_id = segment_index, + .addend = 0, + }); + } + try bind.finalize(gpa, self); } @@ -3620,10 +3649,10 @@ fn writeDysymtab(self: *MachO, ctx: SymtabCtx) !void { const got = &self.sections.items(.header)[sect_id]; got.reserved1 = nstubs; for (self.got_table.entries.items) |entry| { - if (entry.sym_index == 0) continue; - const target_sym = self.getSymbol(entry.target); + if (!self.got_table.lookup.contains(entry)) continue; + const target_sym = self.getSymbol(entry); if (target_sym.undf()) { - try writer.writeIntLittle(u32, iundefsym + ctx.imports_table.get(entry.target).?); + try writer.writeIntLittle(u32, iundefsym + ctx.imports_table.get(entry).?); } else { try writer.writeIntLittle(u32, macho.INDIRECT_SYMBOL_LOCAL); } @@ -4321,7 +4350,7 @@ pub fn logSymtab(self: *MachO) void { } log.debug("GOT entries:", .{}); - log.debug("{}", .{self.got_table.fmtDebug(self)}); + log.debug("{}", .{self.got_table}); log.debug("stubs entries:", .{}); log.debug("{}", .{self.stubs_table.fmtDebug(self)}); diff --git a/src/link/MachO/DebugSymbols.zig b/src/link/MachO/DebugSymbols.zig index 98abf2b1cc..24a0c9ea34 100644 --- a/src/link/MachO/DebugSymbols.zig +++ b/src/link/MachO/DebugSymbols.zig @@ -230,7 +230,7 @@ pub fn flushModule(self: *DebugSymbols, macho_file: *MachO) !void { .got_load => blk: { const got_index = macho_file.got_table.lookup.get(.{ .sym_index = reloc.target }).?; const got_entry = macho_file.got_table.entries.items[got_index]; - break :blk got_entry.getSymbol(macho_file); + break :blk macho_file.getSymbol(got_entry); }, }; if (sym.n_value == reloc.prev_vaddr) continue; @@ -240,7 +240,7 @@ pub fn flushModule(self: *DebugSymbols, macho_file: *MachO) !void { .got_load => blk: { const got_index = macho_file.got_table.lookup.get(.{ .sym_index = reloc.target }).?; const got_entry = macho_file.got_table.entries.items[got_index]; - break :blk got_entry.getName(macho_file); + break :blk macho_file.getSymbolName(got_entry); }, }; const sect = &self.sections.items[self.debug_info_section_index.?]; diff --git a/src/link/MachO/Relocation.zig b/src/link/MachO/Relocation.zig index 81340b1120..77a3de0b43 100644 --- a/src/link/MachO/Relocation.zig +++ b/src/link/MachO/Relocation.zig @@ -39,25 +39,35 @@ pub const Type = enum { /// Returns true if and only if the reloc is dirty AND the target address is available. pub fn isResolvable(self: Relocation, macho_file: *MachO) bool { - _ = self.getTargetAtomIndex(macho_file) orelse return false; + const addr = self.getTargetBaseAddress(macho_file) orelse return false; + if (addr == 0) return false; return self.dirty; } -pub fn getTargetAtomIndex(self: Relocation, macho_file: *MachO) ?Atom.Index { - return switch (self.type) { - .got, .got_page, .got_pageoff => macho_file.got_table.getAtomIndex(macho_file, self.target), - .tlv => { - const thunk_atom_index = macho_file.tlv_table.getAtomIndex(macho_file, self.target) orelse - return null; - const thunk_atom = macho_file.getAtom(thunk_atom_index); - return macho_file.got_table.getAtomIndex(macho_file, thunk_atom.getSymbolWithLoc()); +pub fn getTargetBaseAddress(self: Relocation, macho_file: *MachO) ?u64 { + switch (self.type) { + .got, .got_page, .got_pageoff => { + const got_index = macho_file.got_table.lookup.get(self.target) orelse return null; + const header = macho_file.sections.items(.header)[macho_file.got_section_index.?]; + return header.addr + got_index * @sizeOf(u64); }, - .branch => if (macho_file.stubs_table.getAtomIndex(macho_file, self.target)) |index| - index - else - macho_file.getAtomIndexForSymbol(self.target), - else => macho_file.getAtomIndexForSymbol(self.target), - }; + .tlv => { + const thunk_atom_index = macho_file.tlv_table.getAtomIndex(macho_file, self.target) orelse return null; + const thunk_atom = macho_file.getAtom(thunk_atom_index); + const got_index = macho_file.got_table.lookup.get(thunk_atom.getSymbolWithLoc()) orelse return null; + const header = macho_file.sections.items(.header)[macho_file.got_section_index.?]; + return header.addr + got_index * @sizeOf(u64); + }, + .branch => { + const atom_index = blk: { + if (macho_file.stubs_table.getAtomIndex(macho_file, self.target)) |index| break :blk index; + break :blk macho_file.getAtomIndexForSymbol(self.target) orelse return null; + }; + const atom = macho_file.getAtom(atom_index); + return atom.getSymbol(macho_file).n_value; + }, + else => return macho_file.getSymbol(self.target).n_value, + } } pub fn resolve(self: Relocation, macho_file: *MachO, atom_index: Atom.Index, code: []u8) void { @@ -66,17 +76,14 @@ pub fn resolve(self: Relocation, macho_file: *MachO, atom_index: Atom.Index, cod const source_sym = atom.getSymbol(macho_file); const source_addr = source_sym.n_value + self.offset; - const target_atom_index = self.getTargetAtomIndex(macho_file).?; // Oops, you didn't check if the relocation can be resolved with isResolvable(). - const target_atom = macho_file.getAtom(target_atom_index); - + const target_base_addr = self.getTargetBaseAddress(macho_file).?; // Oops, you didn't check if the relocation can be resolved with isResolvable(). const target_addr: i64 = switch (self.type) { .tlv_initializer => blk: { assert(self.addend == 0); // Addend here makes no sense. const header = macho_file.sections.items(.header)[macho_file.thread_data_section_index.?]; - const target_sym = target_atom.getSymbol(macho_file); - break :blk @intCast(i64, target_sym.n_value - header.addr); + break :blk @intCast(i64, target_base_addr - header.addr); }, - else => @intCast(i64, target_atom.getSymbol(macho_file).n_value) + self.addend, + else => @intCast(i64, target_base_addr) + self.addend, }; log.debug(" ({x}: [() => 0x{x} ({s})) ({s})", .{