From 8a3ad3f6204747b5621e14cae0564ee7929a7cd8 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Tue, 18 Apr 2023 14:04:42 +0200 Subject: elf: do not reserve a GOT slot for every Atom --- src/arch/aarch64/CodeGen.zig | 1 + src/arch/arm/CodeGen.zig | 1 + src/arch/riscv64/CodeGen.zig | 1 + src/arch/sparc64/CodeGen.zig | 1 + src/arch/x86_64/CodeGen.zig | 12 +++- src/codegen.zig | 1 + src/link/Elf.zig | 150 ++++++++++++++++++++++++++++++------------- src/link/Elf/Atom.zig | 18 ++++-- 8 files changed, 132 insertions(+), 53 deletions(-) (limited to 'src') diff --git a/src/arch/aarch64/CodeGen.zig b/src/arch/aarch64/CodeGen.zig index 1acc11d7e8..a2db3459dc 100644 --- a/src/arch/aarch64/CodeGen.zig +++ b/src/arch/aarch64/CodeGen.zig @@ -4290,6 +4290,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier if (self.bin_file.cast(link.File.Elf)) |elf_file| { const atom_index = try elf_file.getOrCreateAtomForDecl(func.owner_decl); const atom = elf_file.getAtom(atom_index); + _ = try atom.getOrCreateOffsetTableEntry(elf_file); const got_addr = @intCast(u32, atom.getOffsetTableAddress(elf_file)); try self.genSetReg(Type.initTag(.usize), .x30, .{ .memory = got_addr }); } else if (self.bin_file.cast(link.File.MachO)) |macho_file| { diff --git a/src/arch/arm/CodeGen.zig b/src/arch/arm/CodeGen.zig index 661e713b1c..156ad380b8 100644 --- a/src/arch/arm/CodeGen.zig +++ b/src/arch/arm/CodeGen.zig @@ -4270,6 +4270,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier if (self.bin_file.cast(link.File.Elf)) |elf_file| { const atom_index = try elf_file.getOrCreateAtomForDecl(func.owner_decl); const atom = elf_file.getAtom(atom_index); + _ = try atom.getOrCreateOffsetTableEntry(elf_file); const got_addr = @intCast(u32, atom.getOffsetTableAddress(elf_file)); try self.genSetReg(Type.initTag(.usize), .lr, .{ .memory = got_addr }); } else if (self.bin_file.cast(link.File.MachO)) |_| { diff --git a/src/arch/riscv64/CodeGen.zig b/src/arch/riscv64/CodeGen.zig index f0ab8b3317..e7dce48dbf 100644 --- a/src/arch/riscv64/CodeGen.zig +++ b/src/arch/riscv64/CodeGen.zig @@ -1734,6 +1734,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier const func = func_payload.data; const atom_index = try elf_file.getOrCreateAtomForDecl(func.owner_decl); const atom = elf_file.getAtom(atom_index); + _ = try atom.getOrCreateOffsetTableEntry(elf_file); const got_addr = @intCast(u32, atom.getOffsetTableAddress(elf_file)); try self.genSetReg(Type.initTag(.usize), .ra, .{ .memory = got_addr }); _ = try self.addInst(.{ diff --git a/src/arch/sparc64/CodeGen.zig b/src/arch/sparc64/CodeGen.zig index 2bcc1e1c4e..beb2ce2fd2 100644 --- a/src/arch/sparc64/CodeGen.zig +++ b/src/arch/sparc64/CodeGen.zig @@ -1254,6 +1254,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier const got_addr = if (self.bin_file.cast(link.File.Elf)) |elf_file| blk: { const atom_index = try elf_file.getOrCreateAtomForDecl(func.owner_decl); const atom = elf_file.getAtom(atom_index); + _ = try atom.getOrCreateOffsetTableEntry(elf_file); break :blk @intCast(u32, atom.getOffsetTableAddress(elf_file)); } else unreachable; diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index 1c72e2296b..e4a32cd5bf 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/src/arch/x86_64/CodeGen.zig @@ -5624,7 +5624,9 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier if (self.bin_file.cast(link.File.Elf)) |elf_file| { const atom_index = try elf_file.getOrCreateAtomForDecl(func.owner_decl); - const got_addr = elf_file.getAtom(atom_index).getOffsetTableAddress(elf_file); + const atom = elf_file.getAtom(atom_index); + _ = try atom.getOrCreateOffsetTableEntry(elf_file); + const got_addr = atom.getOffsetTableAddress(elf_file); try self.asmMemory(.call, Memory.sib(.qword, .{ .base = .ds, .disp = @intCast(i32, got_addr), @@ -5853,7 +5855,9 @@ fn airCmpLtErrorsLen(self: *Self, inst: Air.Inst.Index) !void { .{ .kind = .const_data, .ty = Type.anyerror }, 4, // dword alignment ); - const got_addr = elf_file.getAtom(atom_index).getOffsetTableAddress(elf_file); + const atom = elf_file.getAtom(atom_index); + _ = try atom.getOrCreateOffsetTableEntry(elf_file); + const got_addr = atom.getOffsetTableAddress(elf_file); try self.asmRegisterMemory(.mov, addr_reg.to64(), Memory.sib(.qword, .{ .base = .ds, .disp = @intCast(i32, got_addr), @@ -8230,7 +8234,9 @@ fn airErrorName(self: *Self, inst: Air.Inst.Index) !void { .{ .kind = .const_data, .ty = Type.anyerror }, 4, // dword alignment ); - const got_addr = elf_file.getAtom(atom_index).getOffsetTableAddress(elf_file); + const atom = elf_file.getAtom(atom_index); + _ = try atom.getOrCreateOffsetTableEntry(elf_file); + const got_addr = atom.getOffsetTableAddress(elf_file); try self.asmRegisterMemory(.mov, addr_reg.to64(), Memory.sib(.qword, .{ .base = .ds, .disp = @intCast(i32, got_addr), diff --git a/src/codegen.zig b/src/codegen.zig index 6d6238ceda..dbcd76118a 100644 --- a/src/codegen.zig +++ b/src/codegen.zig @@ -1006,6 +1006,7 @@ fn genDeclRef( if (bin_file.cast(link.File.Elf)) |elf_file| { const atom_index = try elf_file.getOrCreateAtomForDecl(decl_index); const atom = elf_file.getAtom(atom_index); + _ = try atom.getOrCreateOffsetTableEntry(elf_file); return GenResult.mcv(.{ .memory = atom.getOffsetTableAddress(elf_file) }); } else if (bin_file.cast(link.File.MachO)) |macho_file| { const atom_index = try macho_file.getOrCreateAtomForDecl(decl_index); diff --git a/src/link/Elf.zig b/src/link/Elf.zig index b25a6f8f8a..e0bea28bd1 100644 --- a/src/link/Elf.zig +++ b/src/link/Elf.zig @@ -63,6 +63,88 @@ const Section = struct { free_list: std.ArrayListUnmanaged(Atom.Index) = .{}, }; +const SectionTable = struct { + entries: std.ArrayListUnmanaged(SymIndex) = .{}, + free_list: std.ArrayListUnmanaged(Index) = .{}, + lookup: std.AutoHashMapUnmanaged(SymIndex, Index) = .{}, + + const SymIndex = u32; + const Index = u32; + + pub fn deinit(st: *ST, allocator: Allocator) void { + st.entries.deinit(allocator); + st.free_list.deinit(allocator); + st.lookup.deinit(allocator); + } + + pub fn allocateEntry(st: *ST, allocator: Allocator, target: SymIndex) !Index { + try st.entries.ensureUnusedCapacity(allocator, 1); + const index = blk: { + if (st.free_list.popOrNull()) |index| { + log.debug(" (reusing entry index {d})", .{index}); + break :blk index; + } else { + log.debug(" (allocating entry at index {d})", .{st.entries.items.len}); + const index = @intCast(u32, st.entries.items.len); + _ = st.entries.addOneAssumeCapacity(); + break :blk index; + } + }; + st.entries.items[index] = target; + try st.lookup.putNoClobber(allocator, target, index); + return index; + } + + pub fn freeEntry(st: *ST, allocator: Allocator, target: SymIndex) void { + const index = st.lookup.get(target) orelse return; + st.free_list.append(allocator, index) catch {}; + st.entries.items[index] = 0; + _ = st.lookup.remove(target); + } + + const FormatContext = struct { + ctx: *Elf, + st: *const ST, + }; + + fn fmt( + ctx: FormatContext, + comptime unused_format_string: []const u8, + options: std.fmt.FormatOptions, + writer: anytype, + ) @TypeOf(writer).Error!void { + _ = options; + comptime assert(unused_format_string.len == 0); + + const base_addr = ctx.ctx.program_headers.items[ctx.ctx.phdr_got_index.?].p_vaddr; + const target = ctx.ctx.base.options.target; + const ptr_bits = target.cpu.arch.ptrBitWidth(); + const ptr_bytes: u64 = @divExact(ptr_bits, 8); + + try writer.writeAll("SectionTable:\n"); + for (ctx.st.entries.items, 0..) |entry, i| { + try writer.print(" {d}@{x} => local(%{d})\n", .{ i, base_addr + i * ptr_bytes, entry }); + } + } + + fn format(st: ST, comptime unused_format_string: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void { + _ = st; + _ = unused_format_string; + _ = options; + _ = writer; + @compileError("do not format SectionTable directly; use st.fmtDebug()"); + } + + pub fn fmtDebug(st: ST, ctx: *Elf) std.fmt.Formatter(fmt) { + return .{ .data = .{ + .ctx = ctx, + .st = st, + } }; + } + + const ST = @This(); +}; + const LazySymbolMetadata = struct { text_atom: ?Atom.Index = null, rodata_atom: ?Atom.Index = null, @@ -148,17 +230,13 @@ global_symbols: std.ArrayListUnmanaged(elf.Elf64_Sym) = .{}, local_symbol_free_list: std.ArrayListUnmanaged(u32) = .{}, global_symbol_free_list: std.ArrayListUnmanaged(u32) = .{}, -offset_table_free_list: std.ArrayListUnmanaged(u32) = .{}, -/// Same order as in the file. The value is the absolute vaddr value. -/// If the vaddr of the executable program header changes, the entire -/// offset table needs to be rewritten. -offset_table: std.ArrayListUnmanaged(u64) = .{}, +got_table: SectionTable = .{}, phdr_table_dirty: bool = false, shdr_table_dirty: bool = false, shstrtab_dirty: bool = false, -offset_table_count_dirty: bool = false, +got_table_count_dirty: bool = false, debug_strtab_dirty: bool = false, debug_abbrev_section_dirty: bool = false, @@ -329,8 +407,7 @@ pub fn deinit(self: *Elf) void { self.global_symbols.deinit(gpa); self.global_symbol_free_list.deinit(gpa); self.local_symbol_free_list.deinit(gpa); - self.offset_table_free_list.deinit(gpa); - self.offset_table.deinit(gpa); + self.got_table.deinit(gpa); { var it = self.decls.iterator(); @@ -1289,6 +1366,7 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node assert(!self.shdr_table_dirty); assert(!self.shstrtab_dirty); assert(!self.debug_strtab_dirty); + assert(!self.got_table_count_dirty); } fn linkWithLLD(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node) !void { @@ -2168,7 +2246,7 @@ fn freeAtom(self: *Elf, atom_index: Atom.Index) void { _ = self.atom_by_index_table.remove(local_sym_index); self.getAtomPtr(atom_index).local_sym_index = 0; - self.offset_table_free_list.append(self.base.allocator, atom.offset_table_index) catch {}; + self.got_table.freeEntry(gpa, local_sym_index); } fn shrinkAtom(self: *Elf, atom_index: Atom.Index, new_block_size: u64) void { @@ -2191,11 +2269,9 @@ pub fn createAtom(self: *Elf) !Atom.Index { const atom_index = @intCast(Atom.Index, self.atoms.items.len); const atom = try self.atoms.addOne(gpa); const local_sym_index = try self.allocateLocalSymbol(); - const offset_table_index = try self.allocateGotOffset(); try self.atom_by_index_table.putNoClobber(gpa, local_sym_index, atom_index); atom.* = .{ .local_sym_index = local_sym_index, - .offset_table_index = offset_table_index, .prev_index = null, .next_index = null, }; @@ -2352,26 +2428,6 @@ pub fn allocateLocalSymbol(self: *Elf) !u32 { return index; } -pub fn allocateGotOffset(self: *Elf) !u32 { - try self.offset_table.ensureUnusedCapacity(self.base.allocator, 1); - - const index = blk: { - if (self.offset_table_free_list.popOrNull()) |index| { - log.debug(" (reusing GOT offset at index {d})", .{index}); - break :blk index; - } else { - log.debug(" (allocating GOT offset at index {d})", .{self.offset_table.items.len}); - const index = @intCast(u32, self.offset_table.items.len); - _ = self.offset_table.addOneAssumeCapacity(); - self.offset_table_count_dirty = true; - break :blk index; - } - }; - - self.offset_table.items[index] = 0; - return index; -} - fn freeUnnamedConsts(self: *Elf, decl_index: Module.Decl.Index) void { const unnamed_consts = self.unnamed_const_atoms.getPtr(decl_index) orelse return; for (unnamed_consts.items) |atom| { @@ -2465,6 +2521,7 @@ fn updateDeclCode(self: *Elf, decl_index: Module.Decl.Index, code: []const u8, s const decl_metadata = self.decls.get(decl_index).?; const atom_index = decl_metadata.atom; const atom = self.getAtom(atom_index); + const local_sym_index = atom.getSymbolIndex().?; const shdr_index = decl_metadata.shdr; if (atom.getSymbol(self).st_size != 0 and self.base.child_pid == null) { @@ -2485,8 +2542,9 @@ fn updateDeclCode(self: *Elf, decl_index: Module.Decl.Index, code: []const u8, s local_sym.st_value = vaddr; log.debug(" (writing new offset table entry)", .{}); - self.offset_table.items[atom.offset_table_index] = vaddr; - try self.writeOffsetTableEntry(atom.offset_table_index); + const got_entry_index = self.got_table.lookup.get(local_sym_index).?; + self.got_table.entries.items[got_entry_index] = local_sym_index; + try self.writeOffsetTableEntry(got_entry_index); } } else if (code.len < local_sym.st_size) { self.shrinkAtom(atom_index, code.len); @@ -2494,7 +2552,7 @@ fn updateDeclCode(self: *Elf, decl_index: Module.Decl.Index, code: []const u8, s local_sym.st_size = code.len; // TODO this write could be avoided if no fields of the symbol were changed. - try self.writeSymbol(atom.getSymbolIndex().?); + try self.writeSymbol(local_sym_index); } else { const local_sym = atom.getSymbolPtr(self); local_sym.* = .{ @@ -2509,12 +2567,12 @@ fn updateDeclCode(self: *Elf, decl_index: Module.Decl.Index, code: []const u8, s errdefer self.freeAtom(atom_index); log.debug("allocated text block for {s} at 0x{x}", .{ decl_name, vaddr }); - self.offset_table.items[atom.offset_table_index] = vaddr; local_sym.st_value = vaddr; local_sym.st_size = code.len; - try self.writeSymbol(atom.getSymbolIndex().?); - try self.writeOffsetTableEntry(atom.offset_table_index); + try self.writeSymbol(local_sym_index); + const got_entry_index = try atom.getOrCreateOffsetTableEntry(self); + try self.writeOffsetTableEntry(got_entry_index); } const local_sym = atom.getSymbolPtr(self); @@ -2755,12 +2813,12 @@ fn updateLazySymbolAtom( errdefer self.freeAtom(atom_index); log.debug("allocated text block for {s} at 0x{x}", .{ name, vaddr }); - self.offset_table.items[atom.offset_table_index] = vaddr; local_sym.st_value = vaddr; local_sym.st_size = code.len; try self.writeSymbol(local_sym_index); - try self.writeOffsetTableEntry(atom.offset_table_index); + const got_entry_index = try atom.getOrCreateOffsetTableEntry(self); + try self.writeOffsetTableEntry(got_entry_index); const section_offset = vaddr - self.program_headers.items[phdr_index].p_vaddr; const file_offset = self.sections.items(.shdr)[shdr_index].sh_offset + section_offset; @@ -2991,30 +3049,32 @@ fn writeSectHeader(self: *Elf, index: usize) !void { fn writeOffsetTableEntry(self: *Elf, index: usize) !void { const entry_size: u16 = self.archPtrWidthBytes(); - if (self.offset_table_count_dirty) { - const needed_size = self.offset_table.items.len * entry_size; + if (self.got_table_count_dirty) { + const needed_size = self.got_table.entries.items.len * entry_size; try self.growAllocSection(self.got_section_index.?, needed_size); - self.offset_table_count_dirty = false; + self.got_table_count_dirty = false; } const endian = self.base.options.target.cpu.arch.endian(); const shdr = &self.sections.items(.shdr)[self.got_section_index.?]; const off = shdr.sh_offset + @as(u64, entry_size) * index; const phdr = &self.program_headers.items[self.phdr_got_index.?]; const vaddr = phdr.p_vaddr + @as(u64, entry_size) * index; + const got_entry = self.got_table.entries.items[index]; + const got_value = self.getSymbol(got_entry).st_value; switch (entry_size) { 2 => { var buf: [2]u8 = undefined; - mem.writeInt(u16, &buf, @intCast(u16, self.offset_table.items[index]), endian); + mem.writeInt(u16, &buf, @intCast(u16, got_value), endian); try self.base.file.?.pwriteAll(&buf, off); }, 4 => { var buf: [4]u8 = undefined; - mem.writeInt(u32, &buf, @intCast(u32, self.offset_table.items[index]), endian); + mem.writeInt(u32, &buf, @intCast(u32, got_value), endian); try self.base.file.?.pwriteAll(&buf, off); }, 8 => { var buf: [8]u8 = undefined; - mem.writeInt(u64, &buf, self.offset_table.items[index], endian); + mem.writeInt(u64, &buf, got_value, endian); try self.base.file.?.pwriteAll(&buf, off); if (self.base.child_pid) |pid| { diff --git a/src/link/Elf/Atom.zig b/src/link/Elf/Atom.zig index 4ab304ef71..70be5abbca 100644 --- a/src/link/Elf/Atom.zig +++ b/src/link/Elf/Atom.zig @@ -14,9 +14,6 @@ const Elf = @import("../Elf.zig"); /// offset table entry. local_sym_index: u32, -/// This field is undefined for symbols with size = 0. -offset_table_index: u32, - /// Points to the previous and next neighbors, based on the `text_offset`. /// This can be used to find, for example, the capacity of this `TextBlock`. prev_index: ?Index, @@ -48,13 +45,24 @@ pub fn getName(self: Atom, elf_file: *const Elf) []const u8 { return elf_file.getSymbolName(self.getSymbolIndex().?); } +/// If entry already exists, returns index to it. +/// Otherwise, creates a new entry in the Global Offset Table for this Atom. +pub fn getOrCreateOffsetTableEntry(self: Atom, elf_file: *Elf) !u32 { + const sym_index = self.getSymbolIndex().?; + if (elf_file.got_table.lookup.get(sym_index)) |index| return index; + const index = try elf_file.got_table.allocateEntry(elf_file.base.allocator, sym_index); + elf_file.got_table_count_dirty = true; + return index; +} + pub fn getOffsetTableAddress(self: Atom, elf_file: *Elf) u64 { - assert(self.getSymbolIndex() != null); + const sym_index = self.getSymbolIndex().?; + const got_entry_index = elf_file.got_table.lookup.get(sym_index).?; const target = elf_file.base.options.target; const ptr_bits = target.cpu.arch.ptrBitWidth(); const ptr_bytes: u64 = @divExact(ptr_bits, 8); const got = elf_file.program_headers.items[elf_file.phdr_got_index.?]; - return got.p_vaddr + self.offset_table_index * ptr_bytes; + return got.p_vaddr + got_entry_index * ptr_bytes; } /// Returns how much room there is to grow in virtual address space. -- cgit v1.2.3 From 8e3100ae02fef20ce08a679f6212b3d673a5c8f0 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Tue, 18 Apr 2023 17:11:27 +0200 Subject: elf: make TableSection a generic construct --- src/link/Elf.zig | 87 ++-------------------------------------------- src/link/table_section.zig | 65 ++++++++++++++++++++++++++++++++++ 2 files changed, 68 insertions(+), 84 deletions(-) create mode 100644 src/link/table_section.zig (limited to 'src') diff --git a/src/link/Elf.zig b/src/link/Elf.zig index e0bea28bd1..4a6bb99818 100644 --- a/src/link/Elf.zig +++ b/src/link/Elf.zig @@ -30,6 +30,7 @@ const LlvmObject = @import("../codegen/llvm.zig").Object; const Module = @import("../Module.zig"); const Package = @import("../Package.zig"); const StringTable = @import("strtab.zig").StringTable; +const TableSection = @import("table_section.zig").TableSection; const Type = @import("../type.zig").Type; const TypedValue = @import("../TypedValue.zig"); const Value = @import("../value.zig").Value; @@ -63,88 +64,6 @@ const Section = struct { free_list: std.ArrayListUnmanaged(Atom.Index) = .{}, }; -const SectionTable = struct { - entries: std.ArrayListUnmanaged(SymIndex) = .{}, - free_list: std.ArrayListUnmanaged(Index) = .{}, - lookup: std.AutoHashMapUnmanaged(SymIndex, Index) = .{}, - - const SymIndex = u32; - const Index = u32; - - pub fn deinit(st: *ST, allocator: Allocator) void { - st.entries.deinit(allocator); - st.free_list.deinit(allocator); - st.lookup.deinit(allocator); - } - - pub fn allocateEntry(st: *ST, allocator: Allocator, target: SymIndex) !Index { - try st.entries.ensureUnusedCapacity(allocator, 1); - const index = blk: { - if (st.free_list.popOrNull()) |index| { - log.debug(" (reusing entry index {d})", .{index}); - break :blk index; - } else { - log.debug(" (allocating entry at index {d})", .{st.entries.items.len}); - const index = @intCast(u32, st.entries.items.len); - _ = st.entries.addOneAssumeCapacity(); - break :blk index; - } - }; - st.entries.items[index] = target; - try st.lookup.putNoClobber(allocator, target, index); - return index; - } - - pub fn freeEntry(st: *ST, allocator: Allocator, target: SymIndex) void { - const index = st.lookup.get(target) orelse return; - st.free_list.append(allocator, index) catch {}; - st.entries.items[index] = 0; - _ = st.lookup.remove(target); - } - - const FormatContext = struct { - ctx: *Elf, - st: *const ST, - }; - - fn fmt( - ctx: FormatContext, - comptime unused_format_string: []const u8, - options: std.fmt.FormatOptions, - writer: anytype, - ) @TypeOf(writer).Error!void { - _ = options; - comptime assert(unused_format_string.len == 0); - - const base_addr = ctx.ctx.program_headers.items[ctx.ctx.phdr_got_index.?].p_vaddr; - const target = ctx.ctx.base.options.target; - const ptr_bits = target.cpu.arch.ptrBitWidth(); - const ptr_bytes: u64 = @divExact(ptr_bits, 8); - - try writer.writeAll("SectionTable:\n"); - for (ctx.st.entries.items, 0..) |entry, i| { - try writer.print(" {d}@{x} => local(%{d})\n", .{ i, base_addr + i * ptr_bytes, entry }); - } - } - - fn format(st: ST, comptime unused_format_string: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void { - _ = st; - _ = unused_format_string; - _ = options; - _ = writer; - @compileError("do not format SectionTable directly; use st.fmtDebug()"); - } - - pub fn fmtDebug(st: ST, ctx: *Elf) std.fmt.Formatter(fmt) { - return .{ .data = .{ - .ctx = ctx, - .st = st, - } }; - } - - const ST = @This(); -}; - const LazySymbolMetadata = struct { text_atom: ?Atom.Index = null, rodata_atom: ?Atom.Index = null, @@ -231,7 +150,7 @@ global_symbols: std.ArrayListUnmanaged(elf.Elf64_Sym) = .{}, local_symbol_free_list: std.ArrayListUnmanaged(u32) = .{}, global_symbol_free_list: std.ArrayListUnmanaged(u32) = .{}, -got_table: SectionTable = .{}, +got_table: TableSection(u32) = .{}, phdr_table_dirty: bool = false, shdr_table_dirty: bool = false, @@ -3047,7 +2966,7 @@ fn writeSectHeader(self: *Elf, index: usize) !void { } } -fn writeOffsetTableEntry(self: *Elf, index: usize) !void { +fn writeOffsetTableEntry(self: *Elf, index: @TypeOf(self.got_table).Index) !void { const entry_size: u16 = self.archPtrWidthBytes(); if (self.got_table_count_dirty) { const needed_size = self.got_table.entries.items.len * entry_size; diff --git a/src/link/table_section.zig b/src/link/table_section.zig new file mode 100644 index 0000000000..4d5838adb9 --- /dev/null +++ b/src/link/table_section.zig @@ -0,0 +1,65 @@ +pub fn TableSection(comptime Entry: type) type { + return struct { + entries: std.ArrayListUnmanaged(Entry) = .{}, + free_list: std.ArrayListUnmanaged(Index) = .{}, + lookup: std.AutoHashMapUnmanaged(Entry, Index) = .{}, + + pub fn deinit(self: *Self, allocator: Allocator) void { + self.entries.deinit(allocator); + self.free_list.deinit(allocator); + self.lookup.deinit(allocator); + } + + pub fn allocateEntry(self: *Self, allocator: Allocator, entry: Entry) Allocator.Error!Index { + try self.entries.ensureUnusedCapacity(allocator, 1); + const index = blk: { + if (self.free_list.popOrNull()) |index| { + log.debug(" (reusing entry index {d})", .{index}); + break :blk index; + } else { + log.debug(" (allocating entry at index {d})", .{self.entries.items.len}); + const index = @intCast(u32, self.entries.items.len); + _ = self.entries.addOneAssumeCapacity(); + break :blk index; + } + }; + self.entries.items[index] = entry; + try self.lookup.putNoClobber(allocator, entry, index); + return index; + } + + pub fn freeEntry(self: *Self, allocator: Allocator, entry: Entry) void { + const index = self.lookup.get(entry) orelse return; + self.free_list.append(allocator, index) catch {}; + self.entries.items[index] = undefined; + _ = self.lookup.remove(entry); + } + + pub fn count(self: Self) usize { + return self.entries.items.len; + } + + pub fn format( + self: Self, + comptime unused_format_string: []const u8, + options: std.fmt.FormatOptions, + writer: anytype, + ) !void { + _ = options; + comptime assert(unused_format_string.len == 0); + try writer.writeAll("SectionTable:\n"); + for (self.entries.items, 0..) |entry, i| { + try writer.print(" {d} => {}\n", .{ i, entry }); + } + } + + const Self = @This(); + pub const Index = u32; + }; +} + +const std = @import("std"); +const assert = std.debug.assert; +const log = std.log.scoped(.link); + +const Allocator = std.mem.Allocator; -- cgit v1.2.3 From 711bc2cf3919a2010503610574e7dfcad8a17929 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Tue, 18 Apr 2023 17:12:05 +0200 Subject: macho: use generic TableSection for GOT mgmt --- src/link/MachO.zig | 125 +++++++++++++++++++++++++--------------- src/link/MachO/DebugSymbols.zig | 4 +- src/link/MachO/Relocation.zig | 45 +++++++++------ 3 files changed, 105 insertions(+), 69 deletions(-) (limited to 'src') 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), +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); + }, .tlv => { - const thunk_atom_index = macho_file.tlv_table.getAtomIndex(macho_file, self.target) orelse - return null; + 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()); + 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 => if (macho_file.stubs_table.getAtomIndex(macho_file, self.target)) |index| - index - else - macho_file.getAtomIndexForSymbol(self.target), - else => macho_file.getAtomIndexForSymbol(self.target), - }; + .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})", .{ -- cgit v1.2.3 From c6e916d7fe4234c821eeeff19a73b334129c00de Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Tue, 18 Apr 2023 23:00:55 +0200 Subject: macho: fix runtime panics --- src/link/MachO.zig | 48 +++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 47 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/link/MachO.zig b/src/link/MachO.zig index 0efbf3b270..78b6a6c9a1 100644 --- a/src/link/MachO.zig +++ b/src/link/MachO.zig @@ -3266,6 +3266,7 @@ fn collectRebaseData(self: *MachO, rebase: *Rebase) !void { } // Gather GOT pointers + try rebase.entries.ensureUnusedCapacity(gpa, self.got_table.entries.items.len); 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; @@ -3324,6 +3325,7 @@ fn collectBindData(self: *MachO, bind: anytype, raw_bindings: anytype) !void { } // Gather GOT pointers + try bind.entries.ensureUnusedCapacity(gpa, self.got_table.entries.items.len); 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; @@ -3352,6 +3354,50 @@ fn collectBindData(self: *MachO, bind: anytype, raw_bindings: anytype) !void { try bind.finalize(gpa, self); } +fn collectLazyBindData(self: *MachO, bind: anytype, raw_bindings: anytype) !void { + const gpa = self.base.allocator; + const slice = self.sections.slice(); + + for (raw_bindings.keys(), 0..) |atom_index, i| { + const atom = self.getAtom(atom_index); + log.debug(" ATOM(%{?d}, '{s}')", .{ atom.getSymbolIndex(), atom.getName(self) }); + + const sym = atom.getSymbol(self); + const segment_index = slice.items(.segment_index)[sym.n_sect - 1]; + const seg = self.getSegment(sym.n_sect - 1); + + const base_offset = sym.n_value - seg.vmaddr; + + const bindings = raw_bindings.values()[i]; + try bind.entries.ensureUnusedCapacity(gpa, bindings.items.len); + + for (bindings.items) |binding| { + const bind_sym = self.getSymbol(binding.target); + const bind_sym_name = self.getSymbolName(binding.target); + const dylib_ordinal = @divTrunc( + @bitCast(i16, bind_sym.n_desc), + macho.N_SYMBOL_RESOLVER, + ); + log.debug(" | bind at {x}, import('{s}') in dylib({d})", .{ + binding.offset + base_offset, + bind_sym_name, + dylib_ordinal, + }); + if (bind_sym.weakRef()) { + log.debug(" | marking as weak ref ", .{}); + } + bind.entries.appendAssumeCapacity(.{ + .target = binding.target, + .offset = binding.offset + base_offset, + .segment_id = segment_index, + .addend = 0, + }); + } + } + + try bind.finalize(gpa, self); +} + fn collectExportData(self: *MachO, trie: *Trie) !void { const gpa = self.base.allocator; @@ -3395,7 +3441,7 @@ fn writeDyldInfoData(self: *MachO) !void { var lazy_bind = LazyBind{}; defer lazy_bind.deinit(gpa); - try self.collectBindData(&lazy_bind, self.lazy_bindings); + try self.collectLazyBindData(&lazy_bind, self.lazy_bindings); var trie: Trie = .{}; defer trie.deinit(gpa); -- cgit v1.2.3 From 91bb9302e57c62a1340291b8b207b1638b981bc6 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Wed, 19 Apr 2023 11:27:34 +0200 Subject: macho: correctly dirty the GOT table after memory realloc --- src/link/MachO.zig | 45 ++++++++++++++++++++++++++++++++++----------- src/link/table_section.zig | 2 +- 2 files changed, 35 insertions(+), 12 deletions(-) (limited to 'src') diff --git a/src/link/MachO.zig b/src/link/MachO.zig index 78b6a6c9a1..23876a205e 100644 --- a/src/link/MachO.zig +++ b/src/link/MachO.zig @@ -163,6 +163,7 @@ error_flags: File.ErrorFlags = File.ErrorFlags{}, segment_table_dirty: bool = false, got_table_count_dirty: bool = false, +got_table_contents_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. @@ -758,6 +759,13 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No try self.writeAtom(atom_index, code.items); } + if (self.got_table_contents_dirty) { + for (self.got_table.entries.items, 0..) |entry, i| { + if (!self.got_table.lookup.contains(entry)) continue; + try self.writeOffsetTableEntry(i); + } + } + if (build_options.enable_logging) { self.logSymtab(); self.logSections(); @@ -1242,12 +1250,8 @@ pub fn writeAtom(self: *MachO, atom_index: Atom.Index, code: []u8) !void { } if (is_hot_update_compatible) { - if (self.base.child_pid) |pid| blk: { - const task = self.hot_state.mach_task orelse { - log.warn("cannot hot swap: no Mach task acquired for child process with pid {d}", .{pid}); - break :blk; - }; - self.updateAtomInMemory(task, section.segment_index, sym.n_value, code) catch |err| { + if (self.hot_state.mach_task) |task| { + self.writeToMemory(task, section.segment_index, sym.n_value, code) catch |err| { log.warn("cannot hot swap: writing to memory failed: {s}", .{@errorName(err)}); }; } @@ -1262,7 +1266,7 @@ pub fn writeAtom(self: *MachO, atom_index: Atom.Index, code: []u8) !void { } } -fn updateAtomInMemory(self: *MachO, task: std.os.darwin.MachTask, segment_index: u8, addr: u64, code: []const u8) !void { +fn writeToMemory(self: *MachO, task: std.os.darwin.MachTask, segment_index: u8, addr: u64, code: []const u8) !void { const segment = self.segments.items[segment_index]; const cpu_arch = self.base.options.target.cpu.arch; const nwritten = if (!segment.isWriteable()) @@ -1272,7 +1276,7 @@ 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 { +fn writeOffsetTableEntry(self: *MachO, index: usize) !void { const sect_id = self.got_section_index.?; if (self.got_table_count_dirty) { @@ -1282,18 +1286,27 @@ fn writeOffsetTableEntry(self: *MachO, index: @TypeOf(self.got_table).Index) !vo } const header = &self.sections.items(.header)[sect_id]; + const segment_index = self.sections.items(.segment_index)[sect_id]; + const segment = self.getSegment(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; + const vmaddr = segment.vmaddr + entry_offset; + log.warn("writing GOT entry {d}: @{x} => {x}", .{ index, vmaddr, entry_value }); var buf: [8]u8 = undefined; mem.writeIntLittle(u64, &buf, entry_value); try self.base.file.?.pwriteAll(&buf, file_offset); // TODO write in memory + if (is_hot_update_compatible) { + if (self.hot_state.mach_task) |task| { + self.writeToMemory(task, segment_index, vmaddr, &buf) catch |err| { + log.warn("cannot hot swap: writing to memory failed: {s}", .{@errorName(err)}); + }; + } + } } fn writePtrWidthAtom(self: *MachO, atom_index: Atom.Index) !void { @@ -1322,6 +1335,15 @@ fn markRelocsDirtyByAddress(self: *MachO, addr: u64) void { reloc.dirty = true; } } + + // Dirty synthetic table sections if necessary + for (&[_]u8{self.got_section_index.?}, &[_]*bool{&self.got_table_contents_dirty}) |sect_id, dirty| { + if (dirty.*) continue; + const segment_index = self.sections.items(.segment_index)[sect_id]; + const segment = self.segments.items[segment_index]; + if (segment.vmaddr < addr) continue; + dirty.* = true; + } } pub fn allocateSpecialSymbols(self: *MachO) !void { @@ -2097,6 +2119,7 @@ fn addGotEntry(self: *MachO, target: SymbolWithLoc) !void { const got_index = try self.got_table.allocateEntry(self.base.allocator, target); try self.writeOffsetTableEntry(got_index); self.markRelocsDirtyByTarget(target); + self.got_table_count_dirty = true; } fn addStubEntry(self: *MachO, target: SymbolWithLoc) !void { @@ -3010,7 +3033,7 @@ fn growSection(self: *MachO, sect_id: u8, needed_size: u64) !void { const last_atom = self.getAtom(last_atom_index); const sym = last_atom.getSymbol(self); break :blk (sym.n_value + last_atom.size) - segment.vmaddr; - } else 0; + } else header.size; log.debug("moving {s},{s} from 0x{x} to 0x{x}", .{ header.segName(), diff --git a/src/link/table_section.zig b/src/link/table_section.zig index 4d5838adb9..891f3b1a50 100644 --- a/src/link/table_section.zig +++ b/src/link/table_section.zig @@ -47,7 +47,7 @@ pub fn TableSection(comptime Entry: type) type { ) !void { _ = options; comptime assert(unused_format_string.len == 0); - try writer.writeAll("SectionTable:\n"); + try writer.writeAll("TableSection:\n"); for (self.entries.items, 0..) |entry, i| { try writer.print(" {d} => {}\n", .{ i, entry }); } -- cgit v1.2.3 From c55e821df6d6adca449720e052f40463eef8174b Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Wed, 19 Apr 2023 14:20:16 +0200 Subject: macho: resolve relocs before writing to memory (fixes HCS) --- src/link/MachO.zig | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) (limited to 'src') diff --git a/src/link/MachO.zig b/src/link/MachO.zig index 23876a205e..43614e4039 100644 --- a/src/link/MachO.zig +++ b/src/link/MachO.zig @@ -759,11 +759,13 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No try self.writeAtom(atom_index, code.items); } + // Update GOT if it got moved in memory. if (self.got_table_contents_dirty) { for (self.got_table.entries.items, 0..) |entry, i| { if (!self.got_table.lookup.contains(entry)) continue; try self.writeOffsetTableEntry(i); } + self.got_table_contents_dirty = false; } if (build_options.enable_logging) { @@ -1249,6 +1251,8 @@ pub fn writeAtom(self: *MachO, atom_index: Atom.Index, code: []u8) !void { } } + Atom.resolveRelocations(self, atom_index, relocs.items, code); + if (is_hot_update_compatible) { if (self.hot_state.mach_task) |task| { self.writeToMemory(task, section.segment_index, sym.n_value, code) catch |err| { @@ -1257,7 +1261,6 @@ pub fn writeAtom(self: *MachO, atom_index: Atom.Index, code: []u8) !void { } } - Atom.resolveRelocations(self, atom_index, relocs.items, code); try self.base.file.?.pwriteAll(code, file_offset); // Now we can mark the relocs as resolved. @@ -1287,19 +1290,18 @@ fn writeOffsetTableEntry(self: *MachO, index: usize) !void { const header = &self.sections.items(.header)[sect_id]; const segment_index = self.sections.items(.segment_index)[sect_id]; - const segment = self.getSegment(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 = segment.vmaddr + entry_offset; - log.warn("writing GOT entry {d}: @{x} => {x}", .{ index, vmaddr, entry_value }); + const vmaddr = header.addr + entry_offset; + + log.debug("writing GOT entry {d}: @{x} => {x}", .{ index, vmaddr, entry_value }); - var buf: [8]u8 = undefined; + var buf: [@sizeOf(u64)]u8 = undefined; mem.writeIntLittle(u64, &buf, entry_value); try self.base.file.?.pwriteAll(&buf, file_offset); - // TODO write in memory if (is_hot_update_compatible) { if (self.hot_state.mach_task) |task| { self.writeToMemory(task, segment_index, vmaddr, &buf) catch |err| { @@ -2118,7 +2120,6 @@ 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); try self.writeOffsetTableEntry(got_index); - self.markRelocsDirtyByTarget(target); self.got_table_count_dirty = true; } @@ -2542,7 +2543,6 @@ fn updateDeclCode(self: *MachO, decl_index: Module.Decl.Index, code: []u8) !u64 const tlv_atom = self.getAtom(tlv_atom_index); break :blk tlv_atom.getSymbolWithLoc(); } else .{ .sym_index = sym_index }; - self.markRelocsDirtyByTarget(target); log.debug(" (updating GOT entry)", .{}); const got_atom_index = self.got_table.lookup.get(target).?; try self.writeOffsetTableEntry(got_atom_index); -- cgit v1.2.3 From fa40267b04e29fa8e36e73cb54eb68d58d26fa8d Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Wed, 19 Apr 2023 16:38:44 +0200 Subject: macho: do not allocate atoms for stub entries --- src/link/MachO.zig | 453 +++++++++++++++--------------------------- src/link/MachO/Atom.zig | 17 -- src/link/MachO/Relocation.zig | 49 ++++- src/link/MachO/ZldAtom.zig | 75 ++----- src/link/MachO/eh_frame.zig | 3 +- src/link/MachO/stubs.zig | 161 +++++++++++++++ src/link/MachO/thunks.zig | 7 +- src/link/MachO/zld.zig | 130 ++---------- 8 files changed, 408 insertions(+), 487 deletions(-) create mode 100644 src/link/MachO/stubs.zig (limited to 'src') diff --git a/src/link/MachO.zig b/src/link/MachO.zig index 43614e4039..2749cfe095 100644 --- a/src/link/MachO.zig +++ b/src/link/MachO.zig @@ -19,6 +19,7 @@ const fat = @import("MachO/fat.zig"); const link = @import("../link.zig"); const llvm_backend = @import("../codegen/llvm.zig"); const load_commands = @import("MachO/load_commands.zig"); +const stubs = @import("MachO/stubs.zig"); const target_util = @import("../target.zig"); const trace = @import("../tracy.zig").trace; const zld = @import("MachO/zld.zig"); @@ -156,7 +157,7 @@ stub_helper_preamble_atom_index: ?Atom.Index = null, strtab: StringTable(.strtab) = .{}, got_table: TableSection(SymbolWithLoc) = .{}, -stubs_table: SectionTable = .{}, +stub_table: TableSection(SymbolWithLoc) = .{}, tlv_table: SectionTable = .{}, error_flags: File.ErrorFlags = File.ErrorFlags{}, @@ -164,6 +165,8 @@ error_flags: File.ErrorFlags = File.ErrorFlags{}, segment_table_dirty: bool = false, got_table_count_dirty: bool = false, got_table_contents_dirty: bool = false, +stub_table_count_dirty: bool = false, +stub_table_contents_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. @@ -213,11 +216,6 @@ rebases: RebaseTable = .{}, /// this will be a table indexed by index into the list of Atoms. bindings: BindingTable = .{}, -/// A table of lazy bindings indexed by the owning them `Atom`. -/// Note that once we refactor `Atom`'s lifetime and ownership rules, -/// this will be a table indexed by index into the list of Atoms. -lazy_bindings: BindingTable = .{}, - /// Table of tracked LazySymbols. lazy_syms: LazySymbolTable = .{}, @@ -763,11 +761,23 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No if (self.got_table_contents_dirty) { for (self.got_table.entries.items, 0..) |entry, i| { if (!self.got_table.lookup.contains(entry)) continue; + // TODO: write all in one go rather than incrementally. try self.writeOffsetTableEntry(i); } self.got_table_contents_dirty = false; } + // Update stubs if we moved any section in memory. + // TODO: we probably don't need to update all sections if only one got moved. + if (self.stub_table_contents_dirty) { + for (self.stub_table.entries.items, 0..) |entry, i| { + if (!self.stub_table.lookup.contains(entry)) continue; + // TODO: write all in one go rather than incrementally. + try self.writeStubTableEntry(i); + } + self.stub_table_contents_dirty = false; + } + if (build_options.enable_logging) { self.logSymtab(); self.logSections(); @@ -1311,6 +1321,86 @@ fn writeOffsetTableEntry(self: *MachO, index: usize) !void { } } +fn writeStubTableEntry(self: *MachO, index: usize) !void { + const stubs_sect_id = self.stubs_section_index.?; + const stub_helper_sect_id = self.stub_helper_section_index.?; + const laptr_sect_id = self.la_symbol_ptr_section_index.?; + + const cpu_arch = self.base.options.target.cpu.arch; + const stub_entry_size = stubs.calcStubEntrySize(cpu_arch); + const stub_helper_entry_size = stubs.calcStubHelperEntrySize(cpu_arch); + const stub_helper_preamble_size = stubs.calcStubHelperPreambleSize(cpu_arch); + + if (self.stub_table_count_dirty) { + // We grow all 3 sections one by one. + { + const needed_size = stub_entry_size * self.stub_table.entries.items.len; + try self.growSection(stubs_sect_id, needed_size); + } + { + const needed_size = stub_helper_preamble_size + stub_helper_entry_size * self.stub_table.entries.items.len; + try self.growSection(stub_helper_sect_id, needed_size); + } + { + const needed_size = @sizeOf(u64) * self.stub_table.entries.items.len; + try self.growSection(laptr_sect_id, needed_size); + } + self.stub_table_count_dirty = false; + } + + const gpa = self.base.allocator; + + const stubs_header = self.sections.items(.header)[stubs_sect_id]; + const stub_helper_header = self.sections.items(.header)[stub_helper_sect_id]; + const laptr_header = self.sections.items(.header)[laptr_sect_id]; + + const entry = self.stub_table.entries.items[index]; + const stub_addr: u64 = stubs_header.addr + stub_entry_size * index; + const stub_helper_addr: u64 = stub_helper_header.addr + stub_helper_preamble_size + stub_helper_entry_size * index; + const laptr_addr: u64 = laptr_header.addr + @sizeOf(u64) * index; + + log.debug("writing stub entry {d}: @{x} => '{s}'", .{ index, stub_addr, self.getSymbolName(entry) }); + + { + var buf = try std.ArrayList(u8).initCapacity(gpa, stub_entry_size); + defer buf.deinit(); + try stubs.writeStubCode(.{ + .cpu_arch = cpu_arch, + .source_addr = stub_addr, + .target_addr = laptr_addr, + }, buf.writer()); + const off = stubs_header.offset + stub_entry_size * index; + try self.base.file.?.pwriteAll(buf.items, off); + } + + { + var buf = try std.ArrayList(u8).initCapacity(gpa, stub_helper_entry_size); + defer buf.deinit(); + try stubs.writeStubHelperCode(.{ + .cpu_arch = cpu_arch, + .source_addr = stub_helper_addr, + .target_addr = stub_helper_header.addr, + }, buf.writer()); + const off = stub_helper_header.offset + stub_helper_preamble_size + stub_helper_entry_size * index; + try self.base.file.?.pwriteAll(buf.items, off); + } + + { + var buf: [@sizeOf(u64)]u8 = undefined; + mem.writeIntLittle(u64, &buf, stub_helper_addr); + const off = laptr_header.offset + @sizeOf(u64) * index; + try self.base.file.?.pwriteAll(&buf, off); + } + + // TODO: generating new stub entry will require pulling the address of the symbol from the + // target dylib when updating directly in memory. + if (is_hot_update_compatible) { + if (self.hot_state.mach_task) |_| { + @panic("TODO: update a stub entry 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); @@ -1339,12 +1429,16 @@ fn markRelocsDirtyByAddress(self: *MachO, addr: u64) void { } // Dirty synthetic table sections if necessary - for (&[_]u8{self.got_section_index.?}, &[_]*bool{&self.got_table_contents_dirty}) |sect_id, dirty| { - if (dirty.*) continue; - const segment_index = self.sections.items(.segment_index)[sect_id]; - const segment = self.segments.items[segment_index]; - if (segment.vmaddr < addr) continue; - dirty.* = true; + { + const target_addr = self.getSegment(self.got_section_index.?).vmaddr; + if (target_addr >= addr) self.got_table_contents_dirty = true; + } + { + const stubs_addr = self.getSegment(self.stubs_section_index.?).vmaddr; + const stub_helper_addr = self.getSegment(self.stub_helper_section_index.?).vmaddr; + const laptr_addr = self.getSegment(self.la_symbol_ptr_section_index.?).vmaddr; + if (stubs_addr >= addr or stub_helper_addr >= addr or laptr_addr >= addr) + self.stub_table_contents_dirty = true; } } @@ -1525,200 +1619,6 @@ fn createStubHelperPreambleAtom(self: *MachO) !void { try self.writeAtom(atom_index, code); } -fn createStubHelperAtom(self: *MachO) !Atom.Index { - const gpa = self.base.allocator; - const arch = self.base.options.target.cpu.arch; - const size: u4 = switch (arch) { - .x86_64 => 10, - .aarch64 => 3 * @sizeOf(u32), - else => unreachable, - }; - const atom_index = try self.createAtom(); - const atom = self.getAtomPtr(atom_index); - atom.size = size; - - const required_alignment: u32 = switch (arch) { - .x86_64 => 1, - .aarch64 => @alignOf(u32), - else => unreachable, - }; - - const sym = atom.getSymbolPtr(self); - sym.n_type = macho.N_SECT; - sym.n_sect = self.stub_helper_section_index.? + 1; - - const code = try gpa.alloc(u8, size); - defer gpa.free(code); - mem.set(u8, code, 0); - - const stub_helper_preamble_atom_sym_index = if (self.stub_helper_preamble_atom_index) |stub_index| - self.getAtom(stub_index).getSymbolIndex().? - else - unreachable; - - switch (arch) { - .x86_64 => { - // pushq - code[0] = 0x68; - // Next 4 bytes 1..4 are just a placeholder populated in `populateLazyBindOffsetsInStubHelper`. - // jmpq - code[5] = 0xe9; - - try Atom.addRelocation(self, atom_index, .{ - .type = .branch, - .target = .{ .sym_index = stub_helper_preamble_atom_sym_index }, - .offset = 6, - .addend = 0, - .pcrel = true, - .length = 2, - }); - }, - .aarch64 => { - const literal = blk: { - const div_res = try math.divExact(u64, size - @sizeOf(u32), 4); - break :blk math.cast(u18, div_res) orelse return error.Overflow; - }; - // ldr w16, literal - mem.writeIntLittle(u32, code[0..4], aarch64.Instruction.ldrLiteral( - .w16, - literal, - ).toU32()); - // b disp - mem.writeIntLittle(u32, code[4..8], aarch64.Instruction.b(0).toU32()); - // Next 4 bytes 8..12 are just a placeholder populated in `populateLazyBindOffsetsInStubHelper`. - - try Atom.addRelocation(self, atom_index, .{ - .type = .branch, - .target = .{ .sym_index = stub_helper_preamble_atom_sym_index }, - .offset = 4, - .addend = 0, - .pcrel = true, - .length = 2, - }); - }, - else => unreachable, - } - - sym.n_value = try self.allocateAtom(atom_index, size, required_alignment); - log.debug("allocated stub helper atom at 0x{x}", .{sym.n_value}); - try self.writeAtom(atom_index, code); - - return atom_index; -} - -fn createLazyPointerAtom(self: *MachO, stub_sym_index: u32, target: SymbolWithLoc) !Atom.Index { - const atom_index = try self.createAtom(); - const atom = self.getAtomPtr(atom_index); - atom.size = @sizeOf(u64); - - const sym = atom.getSymbolPtr(self); - sym.n_type = macho.N_SECT; - sym.n_sect = self.la_symbol_ptr_section_index.? + 1; - - try Atom.addRelocation(self, atom_index, .{ - .type = .unsigned, - .target = .{ .sym_index = stub_sym_index }, - .offset = 0, - .addend = 0, - .pcrel = false, - .length = 3, - }); - try Atom.addRebase(self, atom_index, 0); - try Atom.addLazyBinding(self, atom_index, .{ - .target = self.getGlobal(self.getSymbolName(target)).?, - .offset = 0, - }); - - sym.n_value = try self.allocateAtom(atom_index, atom.size, @alignOf(u64)); - log.debug("allocated lazy pointer atom at 0x{x} ({s})", .{ sym.n_value, self.getSymbolName(target) }); - try self.writePtrWidthAtom(atom_index); - - return atom_index; -} - -fn createStubAtom(self: *MachO, laptr_sym_index: u32) !Atom.Index { - const gpa = self.base.allocator; - const arch = self.base.options.target.cpu.arch; - const size: u4 = switch (arch) { - .x86_64 => 6, - .aarch64 => 3 * @sizeOf(u32), - else => unreachable, // unhandled architecture type - }; - const atom_index = try self.createAtom(); - const atom = self.getAtomPtr(atom_index); - atom.size = size; - - const required_alignment: u32 = switch (arch) { - .x86_64 => 1, - .aarch64 => @alignOf(u32), - else => unreachable, // unhandled architecture type - - }; - - const sym = atom.getSymbolPtr(self); - sym.n_type = macho.N_SECT; - sym.n_sect = self.stubs_section_index.? + 1; - - const code = try gpa.alloc(u8, size); - defer gpa.free(code); - mem.set(u8, code, 0); - - switch (arch) { - .x86_64 => { - // jmp - code[0] = 0xff; - code[1] = 0x25; - - try Atom.addRelocation(self, atom_index, .{ - .type = .branch, - .target = .{ .sym_index = laptr_sym_index }, - .offset = 2, - .addend = 0, - .pcrel = true, - .length = 2, - }); - }, - .aarch64 => { - // adrp x16, pages - mem.writeIntLittle(u32, code[0..4], aarch64.Instruction.adrp(.x16, 0).toU32()); - // ldr x16, x16, offset - mem.writeIntLittle(u32, code[4..8], aarch64.Instruction.ldr( - .x16, - .x16, - aarch64.Instruction.LoadStoreOffset.imm(0), - ).toU32()); - // br x16 - mem.writeIntLittle(u32, code[8..12], aarch64.Instruction.br(.x16).toU32()); - - try Atom.addRelocations(self, atom_index, &[_]Relocation{ - .{ - .type = .page, - .target = .{ .sym_index = laptr_sym_index }, - .offset = 0, - .addend = 0, - .pcrel = true, - .length = 2, - }, - .{ - .type = .pageoff, - .target = .{ .sym_index = laptr_sym_index }, - .offset = 4, - .addend = 0, - .pcrel = false, - .length = 2, - }, - }); - }, - else => unreachable, - } - - sym.n_value = try self.allocateAtom(atom_index, size, required_alignment); - log.debug("allocated stub atom at 0x{x}", .{sym.n_value}); - try self.writeAtom(atom_index, code); - - return atom_index; -} - fn createThreadLocalDescriptorAtom(self: *MachO, target: SymbolWithLoc) !Atom.Index { const gpa = self.base.allocator; const size = 3 * @sizeOf(u64); @@ -1904,7 +1804,7 @@ pub fn deinit(self: *MachO) void { } self.got_table.deinit(gpa); - self.stubs_table.deinit(gpa); + self.stub_table.deinit(gpa); self.tlv_table.deinit(gpa); self.strtab.deinit(gpa); @@ -1968,11 +1868,6 @@ pub fn deinit(self: *MachO) void { bindings.deinit(gpa); } self.bindings.deinit(gpa); - - for (self.lazy_bindings.values()) |*bindings| { - bindings.deinit(gpa); - } - self.lazy_bindings.deinit(gpa); } fn freeAtom(self: *MachO, atom_index: Atom.Index) void { @@ -2124,16 +2019,10 @@ fn addGotEntry(self: *MachO, target: SymbolWithLoc) !void { } fn addStubEntry(self: *MachO, target: SymbolWithLoc) !void { - if (self.stubs_table.lookup.contains(target)) return; - const stub_index = try self.stubs_table.allocateEntry(self.base.allocator, target); - const stub_helper_atom_index = try self.createStubHelperAtom(); - const stub_helper_atom = self.getAtom(stub_helper_atom_index); - const laptr_atom_index = try self.createLazyPointerAtom(stub_helper_atom.getSymbolIndex().?, target); - const laptr_atom = self.getAtom(laptr_atom_index); - const stub_atom_index = try self.createStubAtom(laptr_atom.getSymbolIndex().?); - const stub_atom = self.getAtom(stub_atom_index); - self.stubs_table.entries.items[stub_index].sym_index = stub_atom.getSymbolIndex().?; - self.markRelocsDirtyByTarget(target); + if (self.stub_table.lookup.contains(target)) return; + const stub_index = try self.stub_table.allocateEntry(self.base.allocator, target); + try self.writeStubTableEntry(stub_index); + self.stub_table_count_dirty = true; } fn addTlvEntry(self: *MachO, target: SymbolWithLoc) !void { @@ -2840,11 +2729,7 @@ fn populateMissingMetadata(self: *MachO) !void { } if (self.stubs_section_index == null) { - const stub_size: u32 = switch (cpu_arch) { - .x86_64 => 6, - .aarch64 => 3 * @sizeOf(u32), - else => unreachable, // unhandled architecture type - }; + const stub_size = stubs.calcStubEntrySize(cpu_arch); self.stubs_section_index = try self.allocateSection("__TEXT2", "__stubs", .{ .size = stub_size, .alignment = switch (cpu_arch) { @@ -3377,45 +3262,35 @@ fn collectBindData(self: *MachO, bind: anytype, raw_bindings: anytype) !void { try bind.finalize(gpa, self); } -fn collectLazyBindData(self: *MachO, bind: anytype, raw_bindings: anytype) !void { +fn collectLazyBindData(self: *MachO, bind: anytype) !void { const gpa = self.base.allocator; - const slice = self.sections.slice(); - for (raw_bindings.keys(), 0..) |atom_index, i| { - const atom = self.getAtom(atom_index); - log.debug(" ATOM(%{?d}, '{s}')", .{ atom.getSymbolIndex(), atom.getName(self) }); - - const sym = atom.getSymbol(self); - const segment_index = slice.items(.segment_index)[sym.n_sect - 1]; - const seg = self.getSegment(sym.n_sect - 1); - - const base_offset = sym.n_value - seg.vmaddr; - - const bindings = raw_bindings.values()[i]; - try bind.entries.ensureUnusedCapacity(gpa, bindings.items.len); - - for (bindings.items) |binding| { - const bind_sym = self.getSymbol(binding.target); - const bind_sym_name = self.getSymbolName(binding.target); - const dylib_ordinal = @divTrunc( - @bitCast(i16, bind_sym.n_desc), - macho.N_SYMBOL_RESOLVER, - ); - log.debug(" | bind at {x}, import('{s}') in dylib({d})", .{ - binding.offset + base_offset, - bind_sym_name, - dylib_ordinal, - }); - if (bind_sym.weakRef()) { - log.debug(" | marking as weak ref ", .{}); - } - bind.entries.appendAssumeCapacity(.{ - .target = binding.target, - .offset = binding.offset + base_offset, - .segment_id = segment_index, - .addend = 0, - }); + try bind.entries.ensureUnusedCapacity(gpa, self.stub_table.entries.items.len); + const segment_index = self.sections.items(.segment_index)[self.la_symbol_ptr_section_index.?]; + for (self.stub_table.entries.items, 0..) |entry, i| { + if (!self.stub_table.lookup.contains(entry)) continue; + const bind_sym = self.getSymbol(entry); + assert(bind_sym.undf()); + const bind_sym_name = self.getSymbolName(entry); + const offset = i * @sizeOf(u64); + 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, + }); + if (bind_sym.weakRef()) { + log.debug(" | marking as weak ref ", .{}); } + bind.entries.appendAssumeCapacity(.{ + .target = entry, + .offset = offset, + .segment_id = segment_index, + .addend = 0, + }); } try bind.finalize(gpa, self); @@ -3464,7 +3339,7 @@ fn writeDyldInfoData(self: *MachO) !void { var lazy_bind = LazyBind{}; defer lazy_bind.deinit(gpa); - try self.collectLazyBindData(&lazy_bind, self.lazy_bindings); + try self.collectLazyBindData(&lazy_bind); var trie: Trie = .{}; defer trie.deinit(gpa); @@ -3542,32 +3417,24 @@ fn populateLazyBindOffsetsInStubHelper(self: *MachO, lazy_bind: LazyBind) !void const stub_helper_section_index = self.stub_helper_section_index.?; assert(self.stub_helper_preamble_atom_index != null); - const section = self.sections.get(stub_helper_section_index); + const header = self.sections.items(.header)[stub_helper_section_index]; - const stub_offset: u4 = switch (self.base.options.target.cpu.arch) { - .x86_64 => 1, - .aarch64 => 2 * @sizeOf(u32), - else => unreachable, - }; - const header = section.header; - var atom_index = section.last_atom_index.?; + const cpu_arch = self.base.options.target.cpu.arch; + const preamble_size = stubs.calcStubHelperPreambleSize(cpu_arch); + const stub_size = stubs.calcStubHelperEntrySize(cpu_arch); + const stub_offset = stubs.calcStubOffsetInStubHelper(cpu_arch); + const base_offset = header.offset + preamble_size; - var index: usize = lazy_bind.offsets.items.len; - while (index > 0) : (index -= 1) { - const atom = self.getAtom(atom_index); - const sym = atom.getSymbol(self); - const file_offset = header.offset + sym.n_value - header.addr + stub_offset; - const bind_offset = lazy_bind.offsets.items[index - 1]; + for (lazy_bind.offsets.items, 0..) |bind_offset, index| { + const file_offset = base_offset + index * stub_size + stub_offset; log.debug("writing lazy bind offset 0x{x} ({s}) in stub helper at 0x{x}", .{ bind_offset, - self.getSymbolName(lazy_bind.entries.items[index - 1].target), + self.getSymbolName(lazy_bind.entries.items[index].target), file_offset, }); try self.base.file.?.pwriteAll(mem.asBytes(&bind_offset), file_offset); - - atom_index = atom.prev_index.?; } } @@ -3683,7 +3550,7 @@ const SymtabCtx = struct { fn writeDysymtab(self: *MachO, ctx: SymtabCtx) !void { const gpa = self.base.allocator; - const nstubs = @intCast(u32, self.stubs_table.lookup.count()); + const nstubs = @intCast(u32, self.stub_table.lookup.count()); const ngot_entries = @intCast(u32, self.got_table.lookup.count()); const nindirectsyms = nstubs * 2 + ngot_entries; const iextdefsym = ctx.nlocalsym; @@ -3704,13 +3571,13 @@ fn writeDysymtab(self: *MachO, ctx: SymtabCtx) !void { const writer = buf.writer(); if (self.stubs_section_index) |sect_id| { - const stubs = &self.sections.items(.header)[sect_id]; - stubs.reserved1 = 0; - for (self.stubs_table.entries.items) |entry| { - if (entry.sym_index == 0) continue; - const target_sym = self.getSymbol(entry.target); + const stubs_header = &self.sections.items(.header)[sect_id]; + stubs_header.reserved1 = 0; + for (self.stub_table.entries.items) |entry| { + if (!self.stub_table.lookup.contains(entry)) continue; + const target_sym = self.getSymbol(entry); assert(target_sym.undf()); - try writer.writeIntLittle(u32, iundefsym + ctx.imports_table.get(entry.target).?); + try writer.writeIntLittle(u32, iundefsym + ctx.imports_table.get(entry).?); } } @@ -3731,11 +3598,11 @@ fn writeDysymtab(self: *MachO, ctx: SymtabCtx) !void { if (self.la_symbol_ptr_section_index) |sect_id| { const la_symbol_ptr = &self.sections.items(.header)[sect_id]; la_symbol_ptr.reserved1 = nstubs + ngot_entries; - for (self.stubs_table.entries.items) |entry| { - if (entry.sym_index == 0) continue; - const target_sym = self.getSymbol(entry.target); + for (self.stub_table.entries.items) |entry| { + if (!self.stub_table.lookup.contains(entry)) continue; + const target_sym = self.getSymbol(entry); assert(target_sym.undf()); - try writer.writeIntLittle(u32, iundefsym + ctx.imports_table.get(entry.target).?); + try writer.writeIntLittle(u32, iundefsym + ctx.imports_table.get(entry).?); } } @@ -4422,7 +4289,7 @@ pub fn logSymtab(self: *MachO) void { log.debug("{}", .{self.got_table}); log.debug("stubs entries:", .{}); - log.debug("{}", .{self.stubs_table.fmtDebug(self)}); + log.debug("{}", .{self.stub_table}); log.debug("threadlocal entries:", .{}); log.debug("{}", .{self.tlv_table.fmtDebug(self)}); diff --git a/src/link/MachO/Atom.zig b/src/link/MachO/Atom.zig index 5b17dc689d..fb05595b7d 100644 --- a/src/link/MachO/Atom.zig +++ b/src/link/MachO/Atom.zig @@ -158,21 +158,6 @@ pub fn addBinding(macho_file: *MachO, atom_index: Index, binding: Binding) !void try gop.value_ptr.append(gpa, binding); } -pub fn addLazyBinding(macho_file: *MachO, atom_index: Index, binding: Binding) !void { - const gpa = macho_file.base.allocator; - const atom = macho_file.getAtom(atom_index); - log.debug(" (adding lazy binding to symbol {s} at offset 0x{x} in %{?d})", .{ - macho_file.getSymbolName(binding.target), - binding.offset, - atom.getSymbolIndex(), - }); - const gop = try macho_file.lazy_bindings.getOrPut(gpa, atom_index); - if (!gop.found_existing) { - gop.value_ptr.* = .{}; - } - try gop.value_ptr.append(gpa, binding); -} - pub fn resolveRelocations( macho_file: *MachO, atom_index: Index, @@ -193,6 +178,4 @@ pub fn freeRelocations(macho_file: *MachO, atom_index: Index) void { if (removed_rebases) |*rebases| rebases.value.deinit(gpa); var removed_bindings = macho_file.bindings.fetchOrderedRemove(atom_index); if (removed_bindings) |*bindings| bindings.value.deinit(gpa); - var removed_lazy_bindings = macho_file.lazy_bindings.fetchOrderedRemove(atom_index); - if (removed_lazy_bindings) |*lazy_bindings| lazy_bindings.value.deinit(gpa); } diff --git a/src/link/MachO/Relocation.zig b/src/link/MachO/Relocation.zig index 77a3de0b43..73f9392d4e 100644 --- a/src/link/MachO/Relocation.zig +++ b/src/link/MachO/Relocation.zig @@ -59,10 +59,12 @@ pub fn getTargetBaseAddress(self: Relocation, macho_file: *MachO) ?u64 { 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; - }; + if (macho_file.stub_table.lookup.get(self.target)) |index| { + const header = macho_file.sections.items(.header)[macho_file.stubs_section_index.?]; + return header.addr + + index * @import("stubs.zig").calcStubEntrySize(macho_file.base.options.target.cpu.arch); + } + const atom_index = macho_file.getAtomIndexForSymbol(self.target) orelse return null; const atom = macho_file.getAtom(atom_index); return atom.getSymbol(macho_file).n_value; }, @@ -196,11 +198,48 @@ fn resolveX8664(self: Relocation, source_addr: u64, target_addr: i64, code: []u8 } } -inline fn isArithmeticOp(inst: *const [4]u8) bool { +pub inline fn isArithmeticOp(inst: *const [4]u8) bool { const group_decode = @truncate(u5, inst[3]); return ((group_decode >> 2) == 4); } +pub fn calcPcRelativeDisplacementX86(source_addr: u64, target_addr: u64, correction: u3) error{Overflow}!i32 { + const disp = @intCast(i64, target_addr) - @intCast(i64, source_addr + 4 + correction); + return math.cast(i32, disp) orelse error.Overflow; +} + +pub fn calcPcRelativeDisplacementArm64(source_addr: u64, target_addr: u64) error{Overflow}!i28 { + const disp = @intCast(i64, target_addr) - @intCast(i64, source_addr); + return math.cast(i28, disp) orelse error.Overflow; +} + +pub fn calcNumberOfPages(source_addr: u64, target_addr: u64) i21 { + const source_page = @intCast(i32, source_addr >> 12); + const target_page = @intCast(i32, target_addr >> 12); + const pages = @intCast(i21, target_page - source_page); + return pages; +} + +pub const PageOffsetInstKind = enum { + arithmetic, + load_store_8, + load_store_16, + load_store_32, + load_store_64, + load_store_128, +}; + +pub fn calcPageOffset(target_addr: u64, kind: PageOffsetInstKind) !u12 { + const narrowed = @truncate(u12, target_addr); + return switch (kind) { + .arithmetic, .load_store_8 => narrowed, + .load_store_16 => try math.divExact(u12, narrowed, 2), + .load_store_32 => try math.divExact(u12, narrowed, 4), + .load_store_64 => try math.divExact(u12, narrowed, 8), + .load_store_128 => try math.divExact(u12, narrowed, 16), + }; +} + const Relocation = @This(); const std = @import("std"); diff --git a/src/link/MachO/ZldAtom.zig b/src/link/MachO/ZldAtom.zig index 7e784ded05..baa6340a13 100644 --- a/src/link/MachO/ZldAtom.zig +++ b/src/link/MachO/ZldAtom.zig @@ -21,6 +21,7 @@ const Allocator = mem.Allocator; const Arch = std.Target.Cpu.Arch; const AtomIndex = @import("zld.zig").AtomIndex; const Object = @import("Object.zig"); +const Relocation = @import("Relocation.zig"); const SymbolWithLoc = @import("zld.zig").SymbolWithLoc; const Zld = @import("zld.zig").Zld; @@ -571,7 +572,7 @@ fn resolveRelocsArm64( zld.getAtom(getRelocTargetAtomIndex(zld, target, is_via_got).?).getFile(), }); - const displacement = if (calcPcRelativeDisplacementArm64( + const displacement = if (Relocation.calcPcRelativeDisplacementArm64( source_addr, zld.getSymbol(actual_target).n_value, )) |disp| blk: { @@ -585,7 +586,7 @@ fn resolveRelocsArm64( actual_target, ).?); log.debug(" | target_addr = 0x{x} (thunk)", .{thunk_sym.n_value}); - break :blk try calcPcRelativeDisplacementArm64(source_addr, thunk_sym.n_value); + break :blk try Relocation.calcPcRelativeDisplacementArm64(source_addr, thunk_sym.n_value); }; const code = atom_code[rel_offset..][0..4]; @@ -607,7 +608,7 @@ fn resolveRelocsArm64( log.debug(" | target_addr = 0x{x}", .{adjusted_target_addr}); - const pages = @bitCast(u21, calcNumberOfPages(source_addr, adjusted_target_addr)); + const pages = @bitCast(u21, Relocation.calcNumberOfPages(source_addr, adjusted_target_addr)); const code = atom_code[rel_offset..][0..4]; var inst = aarch64.Instruction{ .pc_relative_address = mem.bytesToValue(meta.TagPayload( @@ -627,8 +628,8 @@ fn resolveRelocsArm64( log.debug(" | target_addr = 0x{x}", .{adjusted_target_addr}); const code = atom_code[rel_offset..][0..4]; - if (isArithmeticOp(code)) { - const off = try calcPageOffset(adjusted_target_addr, .arithmetic); + if (Relocation.isArithmeticOp(code)) { + const off = try Relocation.calcPageOffset(adjusted_target_addr, .arithmetic); var inst = aarch64.Instruction{ .add_subtract_immediate = mem.bytesToValue(meta.TagPayload( aarch64.Instruction, @@ -644,11 +645,11 @@ fn resolveRelocsArm64( aarch64.Instruction.load_store_register, ), code), }; - const off = try calcPageOffset(adjusted_target_addr, switch (inst.load_store_register.size) { + const off = try Relocation.calcPageOffset(adjusted_target_addr, switch (inst.load_store_register.size) { 0 => if (inst.load_store_register.v == 1) - PageOffsetInstKind.load_store_128 + Relocation.PageOffsetInstKind.load_store_128 else - PageOffsetInstKind.load_store_8, + Relocation.PageOffsetInstKind.load_store_8, 1 => .load_store_16, 2 => .load_store_32, 3 => .load_store_64, @@ -665,7 +666,7 @@ fn resolveRelocsArm64( log.debug(" | target_addr = 0x{x}", .{adjusted_target_addr}); - const off = try calcPageOffset(adjusted_target_addr, .load_store_64); + const off = try Relocation.calcPageOffset(adjusted_target_addr, .load_store_64); var inst: aarch64.Instruction = .{ .load_store_register = mem.bytesToValue(meta.TagPayload( aarch64.Instruction, @@ -689,7 +690,7 @@ fn resolveRelocsArm64( size: u2, }; const reg_info: RegInfo = blk: { - if (isArithmeticOp(code)) { + if (Relocation.isArithmeticOp(code)) { const inst = mem.bytesToValue(meta.TagPayload( aarch64.Instruction, aarch64.Instruction.add_subtract_immediate, @@ -716,7 +717,7 @@ fn resolveRelocsArm64( .load_store_register = .{ .rt = reg_info.rd, .rn = reg_info.rn, - .offset = try calcPageOffset(adjusted_target_addr, .load_store_64), + .offset = try Relocation.calcPageOffset(adjusted_target_addr, .load_store_64), .opc = 0b01, .op1 = 0b01, .v = 0, @@ -726,7 +727,7 @@ fn resolveRelocsArm64( .add_subtract_immediate = .{ .rd = reg_info.rd, .rn = reg_info.rn, - .imm12 = try calcPageOffset(adjusted_target_addr, .arithmetic), + .imm12 = try Relocation.calcPageOffset(adjusted_target_addr, .arithmetic), .sh = 0, .s = 0, .op = 0, @@ -858,7 +859,7 @@ fn resolveRelocsX86( const addend = mem.readIntLittle(i32, atom_code[rel_offset..][0..4]); const adjusted_target_addr = @intCast(u64, @intCast(i64, target_addr) + addend); log.debug(" | target_addr = 0x{x}", .{adjusted_target_addr}); - const disp = try calcPcRelativeDisplacementX86(source_addr, adjusted_target_addr, 0); + const disp = try Relocation.calcPcRelativeDisplacementX86(source_addr, adjusted_target_addr, 0); mem.writeIntLittle(i32, atom_code[rel_offset..][0..4], disp); }, @@ -868,7 +869,7 @@ fn resolveRelocsX86( const addend = mem.readIntLittle(i32, atom_code[rel_offset..][0..4]); const adjusted_target_addr = @intCast(u64, @intCast(i64, target_addr) + addend); log.debug(" | target_addr = 0x{x}", .{adjusted_target_addr}); - const disp = try calcPcRelativeDisplacementX86(source_addr, adjusted_target_addr, 0); + const disp = try Relocation.calcPcRelativeDisplacementX86(source_addr, adjusted_target_addr, 0); mem.writeIntLittle(i32, atom_code[rel_offset..][0..4], disp); }, @@ -876,7 +877,7 @@ fn resolveRelocsX86( const addend = mem.readIntLittle(i32, atom_code[rel_offset..][0..4]); const adjusted_target_addr = @intCast(u64, @intCast(i64, target_addr) + addend); log.debug(" | target_addr = 0x{x}", .{adjusted_target_addr}); - const disp = try calcPcRelativeDisplacementX86(source_addr, adjusted_target_addr, 0); + const disp = try Relocation.calcPcRelativeDisplacementX86(source_addr, adjusted_target_addr, 0); if (zld.tlv_ptr_table.get(target) == null) { // We need to rewrite the opcode from movq to leaq. @@ -913,7 +914,7 @@ fn resolveRelocsX86( log.debug(" | target_addr = 0x{x}", .{adjusted_target_addr}); - const disp = try calcPcRelativeDisplacementX86(source_addr, adjusted_target_addr, correction); + const disp = try Relocation.calcPcRelativeDisplacementX86(source_addr, adjusted_target_addr, correction); mem.writeIntLittle(i32, atom_code[rel_offset..][0..4], disp); }, @@ -955,11 +956,6 @@ fn resolveRelocsX86( } } -inline fn isArithmeticOp(inst: *const [4]u8) bool { - const group_decode = @truncate(u5, inst[3]); - return ((group_decode >> 2) == 4); -} - pub fn getAtomCode(zld: *Zld, atom_index: AtomIndex) []const u8 { const atom = zld.getAtom(atom_index); assert(atom.getFile() != null); // Synthetic atom shouldn't need to inquire for code. @@ -1006,43 +1002,6 @@ pub fn getAtomRelocs(zld: *Zld, atom_index: AtomIndex) []const macho.relocation_ return relocs[cache.start..][0..cache.len]; } -pub fn calcPcRelativeDisplacementX86(source_addr: u64, target_addr: u64, correction: u3) error{Overflow}!i32 { - const disp = @intCast(i64, target_addr) - @intCast(i64, source_addr + 4 + correction); - return math.cast(i32, disp) orelse error.Overflow; -} - -pub fn calcPcRelativeDisplacementArm64(source_addr: u64, target_addr: u64) error{Overflow}!i28 { - const disp = @intCast(i64, target_addr) - @intCast(i64, source_addr); - return math.cast(i28, disp) orelse error.Overflow; -} - -pub fn calcNumberOfPages(source_addr: u64, target_addr: u64) i21 { - const source_page = @intCast(i32, source_addr >> 12); - const target_page = @intCast(i32, target_addr >> 12); - const pages = @intCast(i21, target_page - source_page); - return pages; -} - -const PageOffsetInstKind = enum { - arithmetic, - load_store_8, - load_store_16, - load_store_32, - load_store_64, - load_store_128, -}; - -pub fn calcPageOffset(target_addr: u64, kind: PageOffsetInstKind) !u12 { - const narrowed = @truncate(u12, target_addr); - return switch (kind) { - .arithmetic, .load_store_8 => narrowed, - .load_store_16 => try math.divExact(u12, narrowed, 2), - .load_store_32 => try math.divExact(u12, narrowed, 4), - .load_store_64 => try math.divExact(u12, narrowed, 8), - .load_store_128 => try math.divExact(u12, narrowed, 16), - }; -} - pub fn relocRequiresGot(zld: *Zld, rel: macho.relocation_info) bool { switch (zld.options.target.cpu.arch) { .aarch64 => switch (@intToEnum(macho.reloc_type_arm64, rel.r_type)) { diff --git a/src/link/MachO/eh_frame.zig b/src/link/MachO/eh_frame.zig index 7c5c5b7c25..ea8d4128c2 100644 --- a/src/link/MachO/eh_frame.zig +++ b/src/link/MachO/eh_frame.zig @@ -9,6 +9,7 @@ const log = std.log.scoped(.eh_frame); const Allocator = mem.Allocator; const AtomIndex = @import("zld.zig").AtomIndex; const Atom = @import("ZldAtom.zig"); +const Relocation = @import("Relocation.zig"); const SymbolWithLoc = @import("zld.zig").SymbolWithLoc; const UnwindInfo = @import("UnwindInfo.zig"); const Zld = @import("zld.zig").Zld; @@ -368,7 +369,7 @@ pub fn EhFrameRecord(comptime is_mutable: bool) type { const target_addr = try Atom.getRelocTargetAddress(zld, target, true, false); const addend = mem.readIntLittle(i32, rec.data[rel_offset..][0..4]); const adjusted_target_addr = @intCast(u64, @intCast(i64, target_addr) + addend); - const disp = try Atom.calcPcRelativeDisplacementX86(source_addr, adjusted_target_addr, 0); + const disp = try Relocation.calcPcRelativeDisplacementX86(source_addr, adjusted_target_addr, 0); mem.writeIntLittle(i32, rec.data[rel_offset..][0..4], disp); }, else => unreachable, diff --git a/src/link/MachO/stubs.zig b/src/link/MachO/stubs.zig new file mode 100644 index 0000000000..236ba2cfb8 --- /dev/null +++ b/src/link/MachO/stubs.zig @@ -0,0 +1,161 @@ +const std = @import("std"); +const aarch64 = @import("../../arch/aarch64/bits.zig"); + +const Relocation = @import("Relocation.zig"); + +pub inline fn calcStubHelperPreambleSize(cpu_arch: std.Target.Cpu.Arch) u5 { + return switch (cpu_arch) { + .x86_64 => 15, + .aarch64 => 6 * @sizeOf(u32), + else => unreachable, // unhandled architecture type + }; +} + +pub inline fn calcStubHelperEntrySize(cpu_arch: std.Target.Cpu.Arch) u4 { + return switch (cpu_arch) { + .x86_64 => 10, + .aarch64 => 3 * @sizeOf(u32), + else => unreachable, // unhandled architecture type + }; +} + +pub inline fn calcStubEntrySize(cpu_arch: std.Target.Cpu.Arch) u4 { + return switch (cpu_arch) { + .x86_64 => 6, + .aarch64 => 3 * @sizeOf(u32), + else => unreachable, // unhandled architecture type + }; +} + +pub inline fn calcStubOffsetInStubHelper(cpu_arch: std.Target.Cpu.Arch) u4 { + return switch (cpu_arch) { + .x86_64 => 1, + .aarch64 => 2 * @sizeOf(u32), + else => unreachable, + }; +} + +pub fn writeStubHelperPreambleCode(args: struct { + cpu_arch: std.Target.Cpu.Arch, + source_addr: u64, + dyld_private_addr: u64, + dyld_stub_binder_got_addr: u64, +}, writer: anytype) !void { + switch (args.cpu_arch) { + .x86_64 => { + try writer.writeAll(&.{ 0x4c, 0x8d, 0x1d }); + { + const disp = try Relocation.calcPcRelativeDisplacementX86( + args.source_addr + 3, + args.dyld_private_addr, + 0, + ); + try writer.writeIntLittle(i32, disp); + } + try writer.writeAll(&.{ 0x41, 0x53, 0xff, 0x25 }); + { + const disp = try Relocation.calcPcRelativeDisplacementX86( + args.source_addr + 11, + args.dyld_stub_binder_got_addr, + 0, + ); + try writer.writeIntLittle(i32, disp); + } + }, + .aarch64 => { + { + const pages = Relocation.calcNumberOfPages(args.source_addr, args.dyld_private_addr); + try writer.writeIntLittle(u32, aarch64.Instruction.adrp(.x17, pages).toU32()); + } + { + const off = try Relocation.calcPageOffset(args.dyld_private_addr, .arithmetic); + try writer.writeIntLittle(u32, aarch64.Instruction.add(.x17, .x17, off, false).toU32()); + } + try writer.writeIntLittle(u32, aarch64.Instruction.stp( + .x16, + .x17, + aarch64.Register.sp, + aarch64.Instruction.LoadStorePairOffset.pre_index(-16), + ).toU32()); + { + const pages = Relocation.calcNumberOfPages(args.source_addr + 12, args.dyld_stub_binder_got_addr); + try writer.writeIntLittle(u32, aarch64.Instruction.adrp(.x16, pages).toU32()); + } + { + const off = try Relocation.calcPageOffset(args.dyld_stub_binder_got_addr, .load_store_64); + try writer.writeIntLittle(u32, aarch64.Instruction.ldr( + .x16, + .x16, + aarch64.Instruction.LoadStoreOffset.imm(off), + ).toU32()); + } + try writer.writeIntLittle(u32, aarch64.Instruction.br(.x16).toU32()); + }, + else => unreachable, + } +} + +pub fn writeStubHelperCode(args: struct { + cpu_arch: std.Target.Cpu.Arch, + source_addr: u64, + target_addr: u64, +}, writer: anytype) !void { + switch (args.cpu_arch) { + .x86_64 => { + try writer.writeAll(&.{ 0x68, 0x0, 0x0, 0x0, 0x0, 0xe9 }); + { + const disp = try Relocation.calcPcRelativeDisplacementX86(args.source_addr + 6, args.target_addr, 0); + try writer.writeIntLittle(i32, disp); + } + }, + .aarch64 => { + const stub_size: u4 = 3 * @sizeOf(u32); + const literal = blk: { + const div_res = try std.math.divExact(u64, stub_size - @sizeOf(u32), 4); + break :blk std.math.cast(u18, div_res) orelse return error.Overflow; + }; + try writer.writeIntLittle(u32, aarch64.Instruction.ldrLiteral( + .w16, + literal, + ).toU32()); + { + const disp = try Relocation.calcPcRelativeDisplacementArm64(args.source_addr + 4, args.target_addr); + try writer.writeIntLittle(u32, aarch64.Instruction.b(disp).toU32()); + } + try writer.writeAll(&.{ 0x0, 0x0, 0x0, 0x0 }); + }, + else => unreachable, + } +} + +pub fn writeStubCode(args: struct { + cpu_arch: std.Target.Cpu.Arch, + source_addr: u64, + target_addr: u64, +}, writer: anytype) !void { + switch (args.cpu_arch) { + .x86_64 => { + try writer.writeAll(&.{ 0xff, 0x25 }); + { + const disp = try Relocation.calcPcRelativeDisplacementX86(args.source_addr + 2, args.target_addr, 0); + try writer.writeIntLittle(i32, disp); + } + }, + .aarch64 => { + { + const pages = Relocation.calcNumberOfPages(args.source_addr, args.target_addr); + try writer.writeIntLittle(u32, aarch64.Instruction.adrp(.x16, pages).toU32()); + } + { + const off = try Relocation.calcPageOffset(args.target_addr, .load_store_64); + try writer.writeIntLittle(u32, aarch64.Instruction.ldr( + .x16, + .x16, + aarch64.Instruction.LoadStoreOffset.imm(off), + ).toU32()); + } + try writer.writeIntLittle(u32, aarch64.Instruction.br(.x16).toU32()); + }, + else => unreachable, + } +} diff --git a/src/link/MachO/thunks.zig b/src/link/MachO/thunks.zig index afea08750c..48d1faac6b 100644 --- a/src/link/MachO/thunks.zig +++ b/src/link/MachO/thunks.zig @@ -17,6 +17,7 @@ const aarch64 = @import("../../arch/aarch64/bits.zig"); const Allocator = mem.Allocator; const Atom = @import("ZldAtom.zig"); const AtomIndex = @import("zld.zig").AtomIndex; +const Relocation = @import("Relocation.zig"); const SymbolWithLoc = @import("zld.zig").SymbolWithLoc; const Zld = @import("zld.zig").Zld; @@ -317,7 +318,7 @@ fn isReachable( const source_addr = source_sym.n_value + @intCast(u32, rel.r_address - base_offset); const is_via_got = Atom.relocRequiresGot(zld, rel); const target_addr = Atom.getRelocTargetAddress(zld, target, is_via_got, false) catch unreachable; - _ = Atom.calcPcRelativeDisplacementArm64(source_addr, target_addr) catch + _ = Relocation.calcPcRelativeDisplacementArm64(source_addr, target_addr) catch return false; return true; @@ -364,9 +365,9 @@ pub fn writeThunkCode(zld: *Zld, atom_index: AtomIndex, writer: anytype) !void { if (atom_index == target_atom_index) break zld.getSymbol(target).n_value; } else unreachable; - const pages = Atom.calcNumberOfPages(source_addr, target_addr); + const pages = Relocation.calcNumberOfPages(source_addr, target_addr); try writer.writeIntLittle(u32, aarch64.Instruction.adrp(.x16, pages).toU32()); - const off = try Atom.calcPageOffset(target_addr, .arithmetic); + const off = try Relocation.calcPageOffset(target_addr, .arithmetic); try writer.writeIntLittle(u32, aarch64.Instruction.add(.x16, .x16, off, false).toU32()); try writer.writeIntLittle(u32, aarch64.Instruction.br(.x16).toU32()); } diff --git a/src/link/MachO/zld.zig b/src/link/MachO/zld.zig index 475b3396fd..bc658fc8d2 100644 --- a/src/link/MachO/zld.zig +++ b/src/link/MachO/zld.zig @@ -16,6 +16,7 @@ const link = @import("../../link.zig"); const load_commands = @import("load_commands.zig"); const thunks = @import("thunks.zig"); const trace = @import("../../tracy.zig").trace; +const stub_helpers = @import("stubs.zig"); const Allocator = mem.Allocator; const Archive = @import("Archive.zig"); @@ -666,59 +667,17 @@ pub const Zld = struct { const entry = self.got_entries.items[index]; break :blk entry.getAtomSymbol(self).n_value; }; - switch (cpu_arch) { - .x86_64 => { - try writer.writeAll(&.{ 0x4c, 0x8d, 0x1d }); - { - const disp = try Atom.calcPcRelativeDisplacementX86(source_addr + 3, dyld_private_addr, 0); - try writer.writeIntLittle(i32, disp); - } - try writer.writeAll(&.{ 0x41, 0x53, 0xff, 0x25 }); - { - const disp = try Atom.calcPcRelativeDisplacementX86(source_addr + 11, dyld_stub_binder_got_addr, 0); - try writer.writeIntLittle(i32, disp); - } - }, - .aarch64 => { - { - const pages = Atom.calcNumberOfPages(source_addr, dyld_private_addr); - try writer.writeIntLittle(u32, aarch64.Instruction.adrp(.x17, pages).toU32()); - } - { - const off = try Atom.calcPageOffset(dyld_private_addr, .arithmetic); - try writer.writeIntLittle(u32, aarch64.Instruction.add(.x17, .x17, off, false).toU32()); - } - try writer.writeIntLittle(u32, aarch64.Instruction.stp( - .x16, - .x17, - aarch64.Register.sp, - aarch64.Instruction.LoadStorePairOffset.pre_index(-16), - ).toU32()); - { - const pages = Atom.calcNumberOfPages(source_addr + 12, dyld_stub_binder_got_addr); - try writer.writeIntLittle(u32, aarch64.Instruction.adrp(.x16, pages).toU32()); - } - { - const off = try Atom.calcPageOffset(dyld_stub_binder_got_addr, .load_store_64); - try writer.writeIntLittle(u32, aarch64.Instruction.ldr( - .x16, - .x16, - aarch64.Instruction.LoadStoreOffset.imm(off), - ).toU32()); - } - try writer.writeIntLittle(u32, aarch64.Instruction.br(.x16).toU32()); - }, - else => unreachable, - } + try stub_helpers.writeStubHelperPreambleCode(.{ + .cpu_arch = cpu_arch, + .source_addr = source_addr, + .dyld_private_addr = dyld_private_addr, + .dyld_stub_binder_got_addr = dyld_stub_binder_got_addr, + }, writer); } pub fn createStubHelperAtom(self: *Zld) !AtomIndex { const cpu_arch = self.options.target.cpu.arch; - const stub_size: u4 = switch (cpu_arch) { - .x86_64 => 10, - .aarch64 => 3 * @sizeOf(u32), - else => unreachable, - }; + const stub_size = stub_helpers.calcStubHelperEntrySize(cpu_arch); const alignment: u2 = switch (cpu_arch) { .x86_64 => 0, .aarch64 => 2, @@ -749,32 +708,11 @@ pub const Zld = struct { const sym = self.getSymbol(.{ .sym_index = self.stub_helper_preamble_sym_index.? }); break :blk sym.n_value; }; - switch (cpu_arch) { - .x86_64 => { - try writer.writeAll(&.{ 0x68, 0x0, 0x0, 0x0, 0x0, 0xe9 }); - { - const disp = try Atom.calcPcRelativeDisplacementX86(source_addr + 6, target_addr, 0); - try writer.writeIntLittle(i32, disp); - } - }, - .aarch64 => { - const stub_size: u4 = 3 * @sizeOf(u32); - const literal = blk: { - const div_res = try math.divExact(u64, stub_size - @sizeOf(u32), 4); - break :blk math.cast(u18, div_res) orelse return error.Overflow; - }; - try writer.writeIntLittle(u32, aarch64.Instruction.ldrLiteral( - .w16, - literal, - ).toU32()); - { - const disp = try Atom.calcPcRelativeDisplacementArm64(source_addr + 4, target_addr); - try writer.writeIntLittle(u32, aarch64.Instruction.b(disp).toU32()); - } - try writer.writeAll(&.{ 0x0, 0x0, 0x0, 0x0 }); - }, - else => unreachable, - } + try stub_helpers.writeStubHelperCode(.{ + .cpu_arch = cpu_arch, + .source_addr = source_addr, + .target_addr = target_addr, + }, writer); } pub fn createLazyPointerAtom(self: *Zld) !AtomIndex { @@ -819,11 +757,7 @@ pub const Zld = struct { .aarch64 => 2, else => unreachable, // unhandled architecture type }; - const stub_size: u4 = switch (cpu_arch) { - .x86_64 => 6, - .aarch64 => 3 * @sizeOf(u32), - else => unreachable, // unhandled architecture type - }; + const stub_size = stub_helpers.calcStubEntrySize(cpu_arch); const sym_index = try self.allocateSymbol(); const atom_index = try self.createEmptyAtom(sym_index, stub_size, alignment); const sym = self.getSymbolPtr(.{ .sym_index = sym_index }); @@ -863,31 +797,11 @@ pub const Zld = struct { const sym = self.getSymbol(atom.getSymbolWithLoc()); break :blk sym.n_value; }; - switch (cpu_arch) { - .x86_64 => { - try writer.writeAll(&.{ 0xff, 0x25 }); - { - const disp = try Atom.calcPcRelativeDisplacementX86(source_addr + 2, target_addr, 0); - try writer.writeIntLittle(i32, disp); - } - }, - .aarch64 => { - { - const pages = Atom.calcNumberOfPages(source_addr, target_addr); - try writer.writeIntLittle(u32, aarch64.Instruction.adrp(.x16, pages).toU32()); - } - { - const off = try Atom.calcPageOffset(target_addr, .load_store_64); - try writer.writeIntLittle(u32, aarch64.Instruction.ldr( - .x16, - .x16, - aarch64.Instruction.LoadStoreOffset.imm(off), - ).toU32()); - } - try writer.writeIntLittle(u32, aarch64.Instruction.br(.x16).toU32()); - }, - else => unreachable, - } + try stub_helpers.writeStubCode(.{ + .cpu_arch = cpu_arch, + .source_addr = source_addr, + .target_addr = target_addr, + }, writer); } fn createTentativeDefAtoms(self: *Zld) !void { @@ -2267,11 +2181,7 @@ pub const Zld = struct { assert(self.stub_helper_preamble_sym_index != null); const section = self.sections.get(stub_helper_section_index); - const stub_offset: u4 = switch (self.options.target.cpu.arch) { - .x86_64 => 1, - .aarch64 => 2 * @sizeOf(u32), - else => unreachable, - }; + const stub_offset = stub_helpers.calcStubOffsetInStubHelper(self.options.target.cpu.arch); const header = section.header; var atom_index = section.first_atom_index; atom_index = self.getAtom(atom_index).next_index.?; // skip preamble -- cgit v1.2.3 From cec475531022b0ed13e708f4991c197b2b838b2c Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Wed, 19 Apr 2023 17:48:33 +0200 Subject: macho: do not allocate atom for __stub_helper preamble --- src/link/MachO.zig | 171 +++++++++++------------------------------------------ 1 file changed, 36 insertions(+), 135 deletions(-) (limited to 'src') diff --git a/src/link/MachO.zig b/src/link/MachO.zig index 2749cfe095..3f00cfcbce 100644 --- a/src/link/MachO.zig +++ b/src/link/MachO.zig @@ -152,7 +152,6 @@ globals_free_list: std.ArrayListUnmanaged(u32) = .{}, dyld_stub_binder_index: ?u32 = null, dyld_private_atom_index: ?Atom.Index = null, -stub_helper_preamble_atom_index: ?Atom.Index = null, strtab: StringTable(.strtab) = .{}, @@ -167,6 +166,7 @@ got_table_count_dirty: bool = false, got_table_contents_dirty: bool = false, stub_table_count_dirty: bool = false, stub_table_contents_dirty: bool = false, +stub_helper_preamble_allocated: 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. @@ -723,15 +723,15 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No return error.UndefinedSymbolReference; } - try self.createDyldPrivateAtom(); - try self.createStubHelperPreambleAtom(); - for (actions.items) |action| switch (action.kind) { .none => {}, .add_got => try self.addGotEntry(action.target), .add_stub => try self.addStubEntry(action.target), }; + try self.createDyldPrivateAtom(); + try self.writeStubHelperPreamble(); + try self.allocateSpecialSymbols(); for (self.relocs.keys()) |atom_index| { @@ -1321,6 +1321,35 @@ fn writeOffsetTableEntry(self: *MachO, index: usize) !void { } } +fn writeStubHelperPreamble(self: *MachO) !void { + if (self.stub_helper_preamble_allocated) return; + + const gpa = self.base.allocator; + const cpu_arch = self.base.options.target.cpu.arch; + const size = stubs.calcStubHelperPreambleSize(cpu_arch); + + var buf = try std.ArrayList(u8).initCapacity(gpa, size); + defer buf.deinit(); + + const dyld_private_addr = self.getAtom(self.dyld_private_atom_index.?).getSymbol(self).n_value; + const dyld_stub_binder_got_addr = blk: { + const index = self.got_table.lookup.get(self.getGlobalByIndex(self.dyld_stub_binder_index.?)).?; + const header = self.sections.items(.header)[self.got_section_index.?]; + break :blk header.addr + @sizeOf(u64) * index; + }; + const header = self.sections.items(.header)[self.stub_helper_section_index.?]; + + try stubs.writeStubHelperPreambleCode(.{ + .cpu_arch = cpu_arch, + .source_addr = header.addr, + .dyld_private_addr = dyld_private_addr, + .dyld_stub_binder_got_addr = dyld_stub_binder_got_addr, + }, buf.writer()); + try self.base.file.?.pwriteAll(buf.items, header.offset); + + self.stub_helper_preamble_allocated = true; +} + fn writeStubTableEntry(self: *MachO, index: usize) !void { const stubs_sect_id = self.stubs_section_index.?; const stub_helper_sect_id = self.stub_helper_section_index.?; @@ -1401,11 +1430,6 @@ fn writeStubTableEntry(self: *MachO, index: usize) !void { } } -fn writePtrWidthAtom(self: *MachO, atom_index: Atom.Index) !void { - var buffer: [@sizeOf(u64)]u8 = [_]u8{0} ** @sizeOf(u64); - try self.writeAtom(atom_index, &buffer); -} - fn markRelocsDirtyByTarget(self: *MachO, target: SymbolWithLoc) void { log.debug("marking relocs dirty by target: {}", .{target}); // TODO: reverse-lookup might come in handy here @@ -1492,131 +1516,8 @@ fn createDyldPrivateAtom(self: *MachO) !void { sym.n_value = try self.allocateAtom(atom_index, atom.size, @alignOf(u64)); log.debug("allocated dyld_private atom at 0x{x}", .{sym.n_value}); - try self.writePtrWidthAtom(atom_index); -} - -fn createStubHelperPreambleAtom(self: *MachO) !void { - if (self.stub_helper_preamble_atom_index != null) return; - - const gpa = self.base.allocator; - const arch = self.base.options.target.cpu.arch; - const size: u5 = switch (arch) { - .x86_64 => 15, - .aarch64 => 6 * @sizeOf(u32), - else => unreachable, - }; - const atom_index = try self.createAtom(); - const atom = self.getAtomPtr(atom_index); - atom.size = size; - - const required_alignment: u32 = switch (arch) { - .x86_64 => 1, - .aarch64 => @alignOf(u32), - else => unreachable, - }; - - const sym = atom.getSymbolPtr(self); - sym.n_type = macho.N_SECT; - sym.n_sect = self.stub_helper_section_index.? + 1; - - const dyld_private = self.getAtom(self.dyld_private_atom_index.?).getSymbolWithLoc(); - const dyld_stub_binder = self.globals.items[self.dyld_stub_binder_index.?]; - - const code = try gpa.alloc(u8, size); - defer gpa.free(code); - mem.set(u8, code, 0); - - switch (arch) { - .x86_64 => { - // lea %r11, [rip + disp] - code[0] = 0x4c; - code[1] = 0x8d; - code[2] = 0x1d; - // push %r11 - code[7] = 0x41; - code[8] = 0x53; - // jmp [rip + disp] - code[9] = 0xff; - code[10] = 0x25; - - try Atom.addRelocations(self, atom_index, &[_]Relocation{ .{ - .type = .signed, - .target = dyld_private, - .offset = 3, - .addend = 0, - .pcrel = true, - .length = 2, - }, .{ - .type = .got, - .target = dyld_stub_binder, - .offset = 11, - .addend = 0, - .pcrel = true, - .length = 2, - } }); - }, - - .aarch64 => { - // adrp x17, 0 - mem.writeIntLittle(u32, code[0..][0..4], aarch64.Instruction.adrp(.x17, 0).toU32()); - // add x17, x17, 0 - mem.writeIntLittle(u32, code[4..][0..4], aarch64.Instruction.add(.x17, .x17, 0, false).toU32()); - // stp x16, x17, [sp, #-16]! - mem.writeIntLittle(u32, code[8..][0..4], aarch64.Instruction.stp( - .x16, - .x17, - aarch64.Register.sp, - aarch64.Instruction.LoadStorePairOffset.pre_index(-16), - ).toU32()); - // adrp x16, 0 - mem.writeIntLittle(u32, code[12..][0..4], aarch64.Instruction.adrp(.x16, 0).toU32()); - // ldr x16, [x16, 0] - mem.writeIntLittle(u32, code[16..][0..4], aarch64.Instruction.ldr( - .x16, - .x16, - aarch64.Instruction.LoadStoreOffset.imm(0), - ).toU32()); - // br x16 - mem.writeIntLittle(u32, code[20..][0..4], aarch64.Instruction.br(.x16).toU32()); - - try Atom.addRelocations(self, atom_index, &[_]Relocation{ .{ - .type = .page, - .target = dyld_private, - .offset = 0, - .addend = 0, - .pcrel = true, - .length = 2, - }, .{ - .type = .pageoff, - .target = dyld_private, - .offset = 4, - .addend = 0, - .pcrel = false, - .length = 2, - }, .{ - .type = .got_page, - .target = dyld_stub_binder, - .offset = 12, - .addend = 0, - .pcrel = true, - .length = 2, - }, .{ - .type = .got_pageoff, - .target = dyld_stub_binder, - .offset = 16, - .addend = 0, - .pcrel = false, - .length = 2, - } }); - }, - - else => unreachable, - } - self.stub_helper_preamble_atom_index = atom_index; - - sym.n_value = try self.allocateAtom(atom_index, size, required_alignment); - log.debug("allocated stub preamble atom at 0x{x}", .{sym.n_value}); - try self.writeAtom(atom_index, code); + var buffer: [@sizeOf(u64)]u8 = [_]u8{0} ** @sizeOf(u64); + try self.writeAtom(atom_index, &buffer); } fn createThreadLocalDescriptorAtom(self: *MachO, target: SymbolWithLoc) !Atom.Index { @@ -3415,7 +3316,7 @@ fn populateLazyBindOffsetsInStubHelper(self: *MachO, lazy_bind: LazyBind) !void if (lazy_bind.size() == 0) return; const stub_helper_section_index = self.stub_helper_section_index.?; - assert(self.stub_helper_preamble_atom_index != null); + assert(self.stub_helper_preamble_allocated); const header = self.sections.items(.header)[stub_helper_section_index]; -- cgit v1.2.3 From 9530b95afee9964ee88293cf36e0db882ab301e6 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Wed, 19 Apr 2023 18:01:37 +0200 Subject: macho: refactor common codepath for collecting bindings from TableSection --- src/link/MachO.zig | 91 ++++++++++++++++++++---------------------------------- 1 file changed, 34 insertions(+), 57 deletions(-) (limited to 'src') diff --git a/src/link/MachO.zig b/src/link/MachO.zig index 3f00cfcbce..8b75b35760 100644 --- a/src/link/MachO.zig +++ b/src/link/MachO.zig @@ -3092,6 +3092,37 @@ fn collectRebaseData(self: *MachO, rebase: *Rebase) !void { try rebase.finalize(gpa); } +fn collectBindDataFromTableSection(self: *MachO, sect_id: u8, bind: anytype, table: anytype) !void { + const segment_index = self.sections.items(.segment_index)[sect_id]; + + try bind.entries.ensureUnusedCapacity(self.base.allocator, table.entries.items.len); + + for (table.entries.items, 0..) |entry, i| { + if (!table.lookup.contains(entry)) continue; + + const bind_sym = self.getSymbol(entry); + if (!bind_sym.undf()) continue; + + const offset = i * @sizeOf(u64); + + log.debug(" | bind at {x}, import('{s}') in dylib({d})", .{ + offset, + self.getSymbolName(entry), + @divTrunc(@bitCast(i16, bind_sym.n_desc), macho.N_SYMBOL_RESOLVER), + }); + if (bind_sym.weakRef()) { + log.debug(" | marking as weak ref ", .{}); + } + + bind.entries.appendAssumeCapacity(.{ + .target = entry, + .offset = offset, + .segment_id = segment_index, + .addend = 0, + }); + } +} + fn collectBindData(self: *MachO, bind: anytype, raw_bindings: anytype) !void { const gpa = self.base.allocator; const slice = self.sections.slice(); @@ -3134,67 +3165,13 @@ fn collectBindData(self: *MachO, bind: anytype, raw_bindings: anytype) !void { } // Gather GOT pointers - try bind.entries.ensureUnusedCapacity(gpa, self.got_table.entries.items.len); - 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 self.collectBindDataFromTableSection(self.got_section_index.?, bind, self.got_table); try bind.finalize(gpa, self); } fn collectLazyBindData(self: *MachO, bind: anytype) !void { - const gpa = self.base.allocator; - - try bind.entries.ensureUnusedCapacity(gpa, self.stub_table.entries.items.len); - const segment_index = self.sections.items(.segment_index)[self.la_symbol_ptr_section_index.?]; - for (self.stub_table.entries.items, 0..) |entry, i| { - if (!self.stub_table.lookup.contains(entry)) continue; - const bind_sym = self.getSymbol(entry); - assert(bind_sym.undf()); - const bind_sym_name = self.getSymbolName(entry); - const offset = i * @sizeOf(u64); - 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, - }); - if (bind_sym.weakRef()) { - log.debug(" | marking as weak ref ", .{}); - } - bind.entries.appendAssumeCapacity(.{ - .target = entry, - .offset = offset, - .segment_id = segment_index, - .addend = 0, - }); - } - - try bind.finalize(gpa, self); + try self.collectBindDataFromTableSection(self.la_symbol_ptr_section_index.?, bind, self.stub_table); + try bind.finalize(self.base.allocator, self); } fn collectExportData(self: *MachO, trie: *Trie) !void { -- cgit v1.2.3 From f9e9974c8f67a7452a08973e963f2c06f85424b3 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Wed, 19 Apr 2023 21:00:54 +0200 Subject: macho: skip GOT for TLVs; handle them separately when lowering --- src/arch/x86_64/CodeGen.zig | 2 +- src/link/MachO.zig | 295 +++++++++++++++++------------------------- src/link/MachO/Relocation.zig | 10 +- 3 files changed, 126 insertions(+), 181 deletions(-) (limited to 'src') diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index e4a32cd5bf..f237b98e75 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/src/arch/x86_64/CodeGen.zig @@ -7578,7 +7578,7 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void const atom_index = try self.getSymbolIndexForDecl(self.mod_fn.owner_decl); if (self.bin_file.cast(link.File.MachO)) |_| { _ = try self.addInst(.{ - .tag = .mov_linker, + .tag = .lea_linker, .ops = .tlv_reloc, .data = .{ .payload = try self.addExtra(Mir.LeaRegisterReloc{ .reg = @enumToInt(Register.rdi), diff --git a/src/link/MachO.zig b/src/link/MachO.zig index 8b75b35760..acf6e057d9 100644 --- a/src/link/MachO.zig +++ b/src/link/MachO.zig @@ -157,7 +157,6 @@ strtab: StringTable(.strtab) = .{}, got_table: TableSection(SymbolWithLoc) = .{}, stub_table: TableSection(SymbolWithLoc) = .{}, -tlv_table: SectionTable = .{}, error_flags: File.ErrorFlags = File.ErrorFlags{}, @@ -222,6 +221,10 @@ lazy_syms: LazySymbolTable = .{}, /// Table of tracked Decls. decls: std.AutoArrayHashMapUnmanaged(Module.Decl.Index, DeclMetadata) = .{}, +/// Table of threadlocal variables descriptors. +/// They are emitted in the `__thread_vars` section. +tlv_table: TlvSymbolTable = .{}, + /// Hot-code swapping state. hot_state: if (is_hot_update_compatible) HotUpdateState else struct {} = .{}, @@ -238,6 +241,8 @@ const LazySymbolMetadata = struct { alignment: u32, }; +const TlvSymbolTable = std.AutoArrayHashMapUnmanaged(SymbolWithLoc, Atom.Index); + const DeclMetadata = struct { atom: Atom.Index, section: u8, @@ -266,122 +271,6 @@ const DeclMetadata = struct { } }; -const SectionTable = struct { - entries: std.ArrayListUnmanaged(Entry) = .{}, - free_list: std.ArrayListUnmanaged(u32) = .{}, - lookup: std.AutoHashMapUnmanaged(SymbolWithLoc, u32) = .{}, - - pub fn deinit(st: *ST, allocator: Allocator) void { - st.entries.deinit(allocator); - st.free_list.deinit(allocator); - st.lookup.deinit(allocator); - } - - pub fn allocateEntry(st: *ST, allocator: Allocator, target: SymbolWithLoc) !u32 { - try st.entries.ensureUnusedCapacity(allocator, 1); - const index = blk: { - if (st.free_list.popOrNull()) |index| { - log.debug(" (reusing entry index {d})", .{index}); - break :blk index; - } else { - log.debug(" (allocating entry at index {d})", .{st.entries.items.len}); - const index = @intCast(u32, st.entries.items.len); - _ = st.entries.addOneAssumeCapacity(); - break :blk index; - } - }; - st.entries.items[index] = .{ .target = target, .sym_index = 0 }; - try st.lookup.putNoClobber(allocator, target, index); - return index; - } - - pub fn freeEntry(st: *ST, allocator: Allocator, target: SymbolWithLoc) void { - const index = st.lookup.get(target) orelse return; - st.free_list.append(allocator, index) catch {}; - st.entries.items[index] = .{ - .target = .{ .sym_index = 0 }, - .sym_index = 0, - }; - _ = st.lookup.remove(target); - } - - pub fn getAtomIndex(st: *const ST, macho_file: *MachO, target: SymbolWithLoc) ?Atom.Index { - const index = st.lookup.get(target) orelse return null; - return st.entries.items[index].getAtomIndex(macho_file); - } - - const FormatContext = struct { - macho_file: *MachO, - st: *const ST, - }; - - fn fmt( - ctx: FormatContext, - comptime unused_format_string: []const u8, - options: std.fmt.FormatOptions, - writer: anytype, - ) @TypeOf(writer).Error!void { - _ = options; - comptime assert(unused_format_string.len == 0); - try writer.writeAll("SectionTable:\n"); - for (ctx.st.entries.items, 0..) |entry, i| { - const atom_sym = entry.getSymbol(ctx.macho_file); - const target_sym = ctx.macho_file.getSymbol(entry.target); - try writer.print(" {d}@{x} => ", .{ i, atom_sym.n_value }); - if (target_sym.undf()) { - try writer.print("import('{s}')", .{ - ctx.macho_file.getSymbolName(entry.target), - }); - } else { - try writer.print("local(%{d}) in object({?d})", .{ - entry.target.sym_index, - entry.target.file, - }); - } - try writer.writeByte('\n'); - } - } - - fn format(st: *const ST, comptime unused_format_string: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void { - _ = st; - _ = unused_format_string; - _ = options; - _ = writer; - @compileError("do not format SectionTable directly; use st.fmtDebug()"); - } - - pub fn fmtDebug(st: *const ST, macho_file: *MachO) std.fmt.Formatter(fmt) { - return .{ .data = .{ - .macho_file = macho_file, - .st = st, - } }; - } - - const ST = @This(); - - const Entry = struct { - target: SymbolWithLoc, - // Index into the synthetic symbol table (i.e., file == null). - sym_index: u32, - - pub fn getSymbol(entry: Entry, macho_file: *MachO) macho.nlist_64 { - return macho_file.getSymbol(.{ .sym_index = entry.sym_index }); - } - - pub fn getSymbolPtr(entry: Entry, macho_file: *MachO) *macho.nlist_64 { - return macho_file.getSymbolPtr(.{ .sym_index = entry.sym_index }); - } - - pub fn getAtomIndex(entry: Entry, macho_file: *MachO) ?Atom.Index { - return macho_file.getAtomIndexForSymbol(.{ .sym_index = entry.sym_index }); - } - - pub fn getName(entry: Entry, macho_file: *MachO) []const u8 { - return macho_file.getSymbolName(.{ .sym_index = entry.sym_index }); - } - }; -}; - const BindingTable = std.AutoArrayHashMapUnmanaged(Atom.Index, std.ArrayListUnmanaged(Atom.Binding)); const UnnamedConstTable = std.AutoArrayHashMapUnmanaged(Module.Decl.Index, std.ArrayListUnmanaged(Atom.Index)); const RebaseTable = std.AutoArrayHashMapUnmanaged(Atom.Index, std.ArrayListUnmanaged(u32)); @@ -1520,18 +1409,13 @@ fn createDyldPrivateAtom(self: *MachO) !void { try self.writeAtom(atom_index, &buffer); } -fn createThreadLocalDescriptorAtom(self: *MachO, target: SymbolWithLoc) !Atom.Index { +fn createThreadLocalDescriptorAtom(self: *MachO, sym_name: []const u8, target: SymbolWithLoc) !Atom.Index { const gpa = self.base.allocator; const size = 3 * @sizeOf(u64); const required_alignment: u32 = 1; const atom_index = try self.createAtom(); self.getAtomPtr(atom_index).size = size; - const target_sym_name = self.getSymbolName(target); - const name_delimiter = mem.indexOf(u8, target_sym_name, "$").?; - const sym_name = try gpa.dupe(u8, target_sym_name[0..name_delimiter]); - defer gpa.free(sym_name); - const sym = self.getAtom(atom_index).getSymbolPtr(self); sym.n_type = macho.N_SECT; sym.n_sect = self.thread_vars_section_index.? + 1; @@ -1706,7 +1590,6 @@ pub fn deinit(self: *MachO) void { self.got_table.deinit(gpa); self.stub_table.deinit(gpa); - self.tlv_table.deinit(gpa); self.strtab.deinit(gpa); self.locals.deinit(gpa); @@ -1739,14 +1622,12 @@ pub fn deinit(self: *MachO) void { self.atoms.deinit(gpa); - if (self.base.options.module) |_| { - for (self.decls.values()) |*m| { - m.exports.deinit(gpa); - } - self.decls.deinit(gpa); - } else { - assert(self.decls.count() == 0); + for (self.decls.values()) |*m| { + m.exports.deinit(gpa); } + self.decls.deinit(gpa); + self.lazy_syms.deinit(gpa); + self.tlv_table.deinit(gpa); for (self.unnamed_const_atoms.values()) |*atoms| { atoms.deinit(gpa); @@ -1926,15 +1807,6 @@ fn addStubEntry(self: *MachO, target: SymbolWithLoc) !void { self.stub_table_count_dirty = true; } -fn addTlvEntry(self: *MachO, target: SymbolWithLoc) !void { - if (self.tlv_table.lookup.contains(target)) return; - const tlv_index = try self.tlv_table.allocateEntry(self.base.allocator, target); - const tlv_atom_index = try self.createThreadLocalDescriptorAtom(target); - const tlv_atom = self.getAtom(tlv_atom_index); - self.tlv_table.entries.items[tlv_index].sym_index = tlv_atom.getSymbolIndex().?; - self.markRelocsDirtyByTarget(target); -} - pub fn updateFunc(self: *MachO, module: *Module, func: *Module.Fn, air: Air, liveness: Liveness) !void { if (build_options.skip_non_native and builtin.object_format != .macho) { @panic("Attempted to compile for object format that was disabled by build configuration"); @@ -2081,6 +1953,12 @@ pub fn updateDecl(self: *MachO, module: *Module, decl_index: Module.Decl.Index) } } + const is_threadlocal = if (decl.val.castTag(.variable)) |payload| + payload.data.is_threadlocal and !self.base.options.single_threaded + else + false; + if (is_threadlocal) return self.updateThreadlocalVariable(module, decl_index); + const atom_index = try self.getOrCreateAtomForDecl(decl_index); const sym_index = self.getAtom(atom_index).getSymbolIndex().?; Atom.freeRelocations(self, atom_index); @@ -2229,6 +2107,101 @@ pub fn getOrCreateAtomForLazySymbol(self: *MachO, sym: File.LazySymbol, alignmen return atom.*.?; } +fn updateThreadlocalVariable(self: *MachO, module: *Module, decl_index: Module.Decl.Index) !void { + // Lowering a TLV on macOS involves two stages: + // 1. first we lower the initializer into appopriate section (__thread_data or __thread_bss) + // 2. next, we create a corresponding threadlocal variable descriptor in __thread_vars + + // 1. Lower the initializer value. + const init_atom_index = try self.getOrCreateAtomForDecl(decl_index); + const init_atom = self.getAtomPtr(init_atom_index); + const init_sym_index = init_atom.getSymbolIndex().?; + Atom.freeRelocations(self, init_atom_index); + + const gpa = self.base.allocator; + + var code_buffer = std.ArrayList(u8).init(gpa); + defer code_buffer.deinit(); + + var decl_state: ?Dwarf.DeclState = if (self.d_sym) |*d_sym| + try d_sym.dwarf.initDeclState(module, decl_index) + else + null; + defer if (decl_state) |*ds| ds.deinit(); + + const decl = module.declPtr(decl_index); + const decl_metadata = self.decls.get(decl_index).?; + const decl_val = decl.val.castTag(.variable).?.data.init; + const res = if (decl_state) |*ds| + try codegen.generateSymbol(&self.base, decl.srcLoc(), .{ + .ty = decl.ty, + .val = decl_val, + }, &code_buffer, .{ + .dwarf = ds, + }, .{ + .parent_atom_index = init_sym_index, + }) + else + try codegen.generateSymbol(&self.base, decl.srcLoc(), .{ + .ty = decl.ty, + .val = decl_val, + }, &code_buffer, .none, .{ + .parent_atom_index = init_sym_index, + }); + + var code = switch (res) { + .ok => code_buffer.items, + .fail => |em| { + decl.analysis = .codegen_failure; + try module.failed_decls.put(module.gpa, decl_index, em); + return; + }, + }; + + const required_alignment = decl.getAlignment(self.base.options.target); + + const decl_name = try decl.getFullyQualifiedName(module); + defer gpa.free(decl_name); + + const init_sym_name = try std.fmt.allocPrint(gpa, "{s}$tlv$init", .{decl_name}); + defer gpa.free(init_sym_name); + + const sect_id = decl_metadata.section; + const init_sym = init_atom.getSymbolPtr(self); + init_sym.n_strx = try self.strtab.insert(gpa, init_sym_name); + init_sym.n_type = macho.N_SECT; + init_sym.n_sect = sect_id + 1; + init_sym.n_desc = 0; + init_atom.size = code.len; + + init_sym.n_value = try self.allocateAtom(init_atom_index, code.len, required_alignment); + errdefer self.freeAtom(init_atom_index); + + log.debug("allocated atom for {s} at 0x{x}", .{ init_sym_name, init_sym.n_value }); + log.debug(" (required alignment 0x{x})", .{required_alignment}); + + try self.writeAtom(init_atom_index, code); + + if (decl_state) |*ds| { + try self.d_sym.?.dwarf.commitDeclState( + module, + decl_index, + init_sym.n_value, + self.getAtom(init_atom_index).size, + ds, + ); + } + + try self.updateDeclExports(module, decl_index, module.getDeclExports(decl_index)); + + // 2. Create a TLV descriptor. + const init_atom_sym_loc = init_atom.getSymbolWithLoc(); + const gop = try self.tlv_table.getOrPut(gpa, init_atom_sym_loc); + assert(!gop.found_existing); + gop.value_ptr.* = try self.createThreadLocalDescriptorAtom(decl_name, init_atom_sym_loc); + self.markRelocsDirtyByTarget(init_atom_sym_loc); +} + pub fn getOrCreateAtomForDecl(self: *MachO, decl_index: Module.Decl.Index) !Atom.Index { const gop = try self.decls.getOrPut(self.base.allocator, decl_index); if (!gop.found_existing) { @@ -2296,21 +2269,11 @@ fn updateDeclCode(self: *MachO, decl_index: Module.Decl.Index, code: []u8) !u64 const sect_id = decl_metadata.section; const header = &self.sections.items(.header)[sect_id]; const segment = self.getSegment(sect_id); - const is_threadlocal = if (!self.base.options.single_threaded) - header.flags == macho.S_THREAD_LOCAL_REGULAR or header.flags == macho.S_THREAD_LOCAL_ZEROFILL - else - false; const code_len = code.len; - const sym_name = if (is_threadlocal) - try std.fmt.allocPrint(gpa, "{s}$tlv$init", .{decl_name}) - else - decl_name; - defer if (is_threadlocal) gpa.free(sym_name); - if (atom.size != 0) { const sym = atom.getSymbolPtr(self); - sym.n_strx = try self.strtab.insert(gpa, sym_name); + sym.n_strx = try self.strtab.insert(gpa, decl_name); sym.n_type = macho.N_SECT; sym.n_sect = sect_id + 1; sym.n_desc = 0; @@ -2320,21 +2283,13 @@ fn updateDeclCode(self: *MachO, decl_index: Module.Decl.Index, code: []u8) !u64 if (need_realloc) { const vaddr = try self.growAtom(atom_index, code_len, required_alignment); - log.debug("growing {s} and moving from 0x{x} to 0x{x}", .{ sym_name, sym.n_value, vaddr }); + log.debug("growing {s} and moving from 0x{x} to 0x{x}", .{ decl_name, sym.n_value, vaddr }); log.debug(" (required alignment 0x{x})", .{required_alignment}); if (vaddr != sym.n_value) { sym.n_value = vaddr; - // TODO: I think we should update the offset to the initializer here too. - const target: SymbolWithLoc = if (is_threadlocal) blk: { - const tlv_atom_index = self.tlv_table.getAtomIndex(self, .{ - .sym_index = sym_index, - }).?; - const tlv_atom = self.getAtom(tlv_atom_index); - break :blk tlv_atom.getSymbolWithLoc(); - } else .{ .sym_index = sym_index }; log.debug(" (updating GOT entry)", .{}); - const got_atom_index = self.got_table.lookup.get(target).?; + const got_atom_index = self.got_table.lookup.get(.{ .sym_index = sym_index }).?; try self.writeOffsetTableEntry(got_atom_index); } } else if (code_len < atom.size) { @@ -2346,7 +2301,7 @@ fn updateDeclCode(self: *MachO, decl_index: Module.Decl.Index, code: []u8) !u64 self.getAtomPtr(atom_index).size = code_len; } else { const sym = atom.getSymbolPtr(self); - sym.n_strx = try self.strtab.insert(gpa, sym_name); + sym.n_strx = try self.strtab.insert(gpa, decl_name); sym.n_type = macho.N_SECT; sym.n_sect = sect_id + 1; sym.n_desc = 0; @@ -2354,21 +2309,13 @@ fn updateDeclCode(self: *MachO, decl_index: Module.Decl.Index, code: []u8) !u64 const vaddr = try self.allocateAtom(atom_index, code_len, required_alignment); errdefer self.freeAtom(atom_index); - log.debug("allocated atom for {s} at 0x{x}", .{ sym_name, vaddr }); + log.debug("allocated atom for {s} at 0x{x}", .{ decl_name, vaddr }); log.debug(" (required alignment 0x{x})", .{required_alignment}); self.getAtomPtr(atom_index).size = code_len; sym.n_value = vaddr; - if (is_threadlocal) { - try self.addTlvEntry(.{ .sym_index = sym_index }); - } - const target: SymbolWithLoc = if (is_threadlocal) blk: { - const tlv_atom_index = self.tlv_table.getAtomIndex(self, .{ .sym_index = sym_index }).?; - const tlv_atom = self.getAtom(tlv_atom_index); - break :blk tlv_atom.getSymbolWithLoc(); - } else .{ .sym_index = sym_index }; - try self.addGotEntry(target); + try self.addGotEntry(.{ .sym_index = sym_index }); } try self.writeAtom(atom_index, code); @@ -4169,8 +4116,8 @@ pub fn logSymtab(self: *MachO) void { log.debug("stubs entries:", .{}); log.debug("{}", .{self.stub_table}); - log.debug("threadlocal entries:", .{}); - log.debug("{}", .{self.tlv_table.fmtDebug(self)}); + // log.debug("threadlocal entries:", .{}); + // log.debug("{}", .{self.tlv_table}); } pub fn logAtoms(self: *MachO) void { diff --git a/src/link/MachO/Relocation.zig b/src/link/MachO/Relocation.zig index 73f9392d4e..e511901009 100644 --- a/src/link/MachO/Relocation.zig +++ b/src/link/MachO/Relocation.zig @@ -15,7 +15,7 @@ pub const Type = enum { got, /// RIP-relative displacement signed, - /// RIP-relative displacement to GOT pointer to TLV thunk + /// RIP-relative displacement to a TLV thunk tlv, // aarch64 @@ -52,11 +52,9 @@ pub fn getTargetBaseAddress(self: Relocation, macho_file: *MachO) ?u64 { return header.addr + got_index * @sizeOf(u64); }, .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); + const atom_index = macho_file.tlv_table.get(self.target) orelse return null; + const atom = macho_file.getAtom(atom_index); + return atom.getSymbol(macho_file).n_value; }, .branch => { if (macho_file.stub_table.lookup.get(self.target)) |index| { -- cgit v1.2.3 From d6e66cff12d9f446c592e90db31642d503e29ade Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Wed, 19 Apr 2023 21:53:30 +0200 Subject: macho: fix dirtying of GOT entries --- src/link/MachO.zig | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) (limited to 'src') diff --git a/src/link/MachO.zig b/src/link/MachO.zig index acf6e057d9..4a3b76dd27 100644 --- a/src/link/MachO.zig +++ b/src/link/MachO.zig @@ -1335,17 +1335,20 @@ fn markRelocsDirtyByAddress(self: *MachO, addr: u64) void { for (self.relocs.values()) |*relocs| { for (relocs.items) |*reloc| { const target_addr = reloc.getTargetBaseAddress(self) orelse continue; - if (target_addr == 0) continue; if (target_addr < addr) continue; reloc.dirty = true; } } - // Dirty synthetic table sections if necessary - { - const target_addr = self.getSegment(self.got_section_index.?).vmaddr; - if (target_addr >= addr) self.got_table_contents_dirty = true; + // TODO: dirty only really affected GOT cells + for (self.got_table.entries.items) |entry| { + const target_addr = self.getSymbol(entry).n_value; + if (target_addr >= addr) { + self.got_table_contents_dirty = true; + break; + } } + { const stubs_addr = self.getSegment(self.stubs_section_index.?).vmaddr; const stub_helper_addr = self.getSegment(self.stub_helper_section_index.?).vmaddr; -- cgit v1.2.3 From ae8fb2151425dc7f61ad46e4c13b6c61f67cc68b Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Thu, 20 Apr 2023 11:18:27 +0200 Subject: coff: use TableSection for GOT --- src/link/Coff.zig | 247 ++++++++++++++++++++++--------------------- src/link/Coff/Relocation.zig | 15 ++- 2 files changed, 131 insertions(+), 131 deletions(-) (limited to 'src') diff --git a/src/link/Coff.zig b/src/link/Coff.zig index 825afff36d..00da6f02e0 100644 --- a/src/link/Coff.zig +++ b/src/link/Coff.zig @@ -37,13 +37,14 @@ strtab_offset: ?u32 = null, temp_strtab: StringTable(.temp_strtab) = .{}, -got_entries: std.ArrayListUnmanaged(Entry) = .{}, -got_entries_free_list: std.ArrayListUnmanaged(u32) = .{}, -got_entries_table: std.AutoHashMapUnmanaged(SymbolWithLoc, u32) = .{}, +got_table: TableSection(SymbolWithLoc) = .{}, /// A table of ImportTables partitioned by the library name. /// Key is an offset into the interning string table `temp_strtab`. import_tables: std.AutoArrayHashMapUnmanaged(u32, ImportTable) = .{}, + +got_table_count_dirty: bool = true, +got_table_contents_dirty: bool = true, imports_count_dirty: bool = true, /// Virtual address of the entry point procedure relative to image base. @@ -188,7 +189,8 @@ pub const PtrWidth = enum { p32, p64, - fn abiSize(pw: PtrWidth) u4 { + /// Size in bytes. + pub fn size(pw: PtrWidth) u4 { return switch (pw) { .p32 => 4, .p64 => 8, @@ -310,9 +312,7 @@ pub fn deinit(self: *Coff) void { self.globals_free_list.deinit(gpa); self.strtab.deinit(gpa); self.temp_strtab.deinit(gpa); - self.got_entries.deinit(gpa); - self.got_entries_free_list.deinit(gpa); - self.got_entries_table.deinit(gpa); + self.got_table.deinit(gpa); for (self.import_tables.values()) |*itab| { itab.deinit(gpa); @@ -371,7 +371,7 @@ fn populateMissingMetadata(self: *Coff) !void { } if (self.got_section_index == null) { - const file_size = @intCast(u32, self.base.options.symbol_count_hint) * self.ptr_width.abiSize(); + const file_size = @intCast(u32, self.base.options.symbol_count_hint) * self.ptr_width.size(); self.got_section_index = try self.allocateSection(".got", file_size, .{ .CNT_INITIALIZED_DATA = 1, .MEM_READ = 1, @@ -396,7 +396,7 @@ fn populateMissingMetadata(self: *Coff) !void { } if (self.idata_section_index == null) { - const file_size = @intCast(u32, self.base.options.symbol_count_hint) * self.ptr_width.abiSize(); + const file_size = @intCast(u32, self.base.options.symbol_count_hint) * self.ptr_width.size(); self.idata_section_index = try self.allocateSection(".idata", file_size, .{ .CNT_INITIALIZED_DATA = 1, .MEM_READ = 1, @@ -698,26 +698,11 @@ fn allocateGlobal(self: *Coff) !u32 { return index; } -pub fn allocateGotEntry(self: *Coff, target: SymbolWithLoc) !u32 { - const gpa = self.base.allocator; - try self.got_entries.ensureUnusedCapacity(gpa, 1); - - const index: u32 = blk: { - if (self.got_entries_free_list.popOrNull()) |index| { - log.debug(" (reusing GOT entry index {d})", .{index}); - break :blk index; - } else { - log.debug(" (allocating GOT entry at index {d})", .{self.got_entries.items.len}); - const index = @intCast(u32, self.got_entries.items.len); - _ = self.got_entries.addOneAssumeCapacity(); - break :blk index; - } - }; - - self.got_entries.items[index] = .{ .target = target, .sym_index = 0 }; - try self.got_entries_table.putNoClobber(gpa, target, index); - - return index; +fn addGotEntry(self: *Coff, target: SymbolWithLoc) !void { + if (self.got_table.lookup.contains(target)) return; + const got_index = try self.got_table.allocateEntry(self.base.allocator, target); + try self.writeOffsetTableEntry(got_index); + self.got_table_count_dirty = true; } pub fn createAtom(self: *Coff) !Atom.Index { @@ -737,37 +722,6 @@ pub fn createAtom(self: *Coff) !Atom.Index { return atom_index; } -fn createGotAtom(self: *Coff, target: SymbolWithLoc) !Atom.Index { - const atom_index = try self.createAtom(); - const atom = self.getAtomPtr(atom_index); - atom.size = @sizeOf(u64); - - const sym = atom.getSymbolPtr(self); - sym.section_number = @intToEnum(coff.SectionNumber, self.got_section_index.? + 1); - sym.value = try self.allocateAtom(atom_index, atom.size, @sizeOf(u64)); - - log.debug("allocated GOT atom at 0x{x}", .{sym.value}); - - try Atom.addRelocation(self, atom_index, .{ - .type = .direct, - .target = target, - .offset = 0, - .addend = 0, - .pcrel = false, - .length = 3, - }); - - const target_sym = self.getSymbol(target); - switch (target_sym.section_number) { - .UNDEFINED => @panic("TODO generate a binding for undefined GOT target"), - .ABSOLUTE => {}, - .DEBUG => unreachable, // not possible - else => try Atom.addBaseRelocation(self, atom_index, 0), - } - - return atom_index; -} - fn growAtom(self: *Coff, atom_index: Atom.Index, new_atom_size: u32, alignment: u32) !u32 { const atom = self.getAtom(atom_index); const sym = atom.getSymbol(self); @@ -873,17 +827,73 @@ fn writeMem(handle: std.ChildProcess.Id, pvaddr: std.os.windows.LPVOID, code: [] if (amt != code.len) return error.InputOutput; } -fn writePtrWidthAtom(self: *Coff, atom_index: Atom.Index) !void { +fn writeOffsetTableEntry(self: *Coff, index: usize) !void { + const sect_id = self.got_section_index.?; + + if (self.got_table_count_dirty) { + const needed_size = @intCast(u32, self.got_table.entries.items.len * self.ptr_width.size()); + 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).value; + const entry_offset = index * self.ptr_width.size(); + const file_offset = header.pointer_to_raw_data + entry_offset; + const vmaddr = header.virtual_address + entry_offset; + + log.debug("writing GOT entry {d}: @{x} => {x}", .{ index, vmaddr, entry_value }); + switch (self.ptr_width) { .p32 => { - var buffer: [@sizeOf(u32)]u8 = [_]u8{0} ** @sizeOf(u32); - try self.writeAtom(atom_index, &buffer); + var buf: [4]u8 = undefined; + mem.writeIntLittle(u32, &buf, @intCast(u32, entry_value)); + try self.base.file.?.pwriteAll(&buf, file_offset); }, .p64 => { - var buffer: [@sizeOf(u64)]u8 = [_]u8{0} ** @sizeOf(u64); - try self.writeAtom(atom_index, &buffer); + var buf: [8]u8 = undefined; + mem.writeIntLittle(u64, &buf, entry_value); + try self.base.file.?.pwriteAll(&buf, file_offset); }, } + + if (is_hot_update_compatible) { + if (self.base.child_pid) |handle| { + const gpa = self.base.allocator; + const slide = @ptrToInt(self.hot_state.loaded_base_address.?); + const actual_vmaddr = vmaddr + slide; + const pvaddr = @intToPtr(*anyopaque, actual_vmaddr); + log.debug("writing GOT entry to memory at address {x}", .{actual_vmaddr}); + if (build_options.enable_logging) { + switch (self.ptr_width) { + .p32 => { + var buf: [4]u8 = undefined; + try debugMem(gpa, handle, pvaddr, &buf); + }, + .p64 => { + var buf: [8]u8 = undefined; + try debugMem(gpa, handle, pvaddr, &buf); + }, + } + } + + switch (self.ptr_width) { + .p32 => { + var buf: [4]u8 = undefined; + writeMem(handle, pvaddr, &buf) catch |err| { + log.warn("writing to protected memory failed with error: {s}", .{@errorName(err)}); + }; + }, + .p64 => { + var buf: [8]u8 = undefined; + writeMem(handle, pvaddr, &buf) catch |err| { + log.warn("writing to protected memory failed with error: {s}", .{@errorName(err)}); + }; + }, + } + } + } } fn markRelocsDirtyByTarget(self: *Coff, target: SymbolWithLoc) void { @@ -904,6 +914,15 @@ fn markRelocsDirtyByAddress(self: *Coff, addr: u32) void { reloc.dirty = true; } } + + // TODO: dirty only really affected GOT cells + for (self.got_table.entries.items) |entry| { + const target_addr = self.getSymbol(entry).value; + if (target_addr >= addr) { + self.got_table_contents_dirty = true; + break; + } + } } fn resolveRelocs(self: *Coff, atom_index: Atom.Index, relocs: []*const Relocation, code: []u8, image_base: u64) void { @@ -994,17 +1013,7 @@ fn freeAtom(self: *Coff, atom_index: Atom.Index) void { self.locals_free_list.append(gpa, sym_index) catch {}; // Try freeing GOT atom if this decl had one - const got_target = SymbolWithLoc{ .sym_index = sym_index, .file = null }; - if (self.got_entries_table.get(got_target)) |got_index| { - self.got_entries_free_list.append(gpa, @intCast(u32, got_index)) catch {}; - self.got_entries.items[got_index] = .{ - .target = .{ .sym_index = 0, .file = null }, - .sym_index = 0, - }; - _ = self.got_entries_table.remove(got_target); - - log.debug(" adding GOT index {d} to free list (target local@{d})", .{ got_index, sym_index }); - } + self.got_table.freeEntry(gpa, .{ .sym_index = sym_index }); self.locals.items[sym_index].section_number = .UNDEFINED; _ = self.atom_by_index_table.remove(sym_index); @@ -1243,14 +1252,7 @@ fn updateLazySymbolAtom( atom.size = code_len; symbol.value = vaddr; - const got_target = SymbolWithLoc{ .sym_index = local_sym_index, .file = null }; - const got_index = try self.allocateGotEntry(got_target); - const got_atom_index = try self.createGotAtom(got_target); - const got_atom = self.getAtom(got_atom_index); - self.got_entries.items[got_index].sym_index = got_atom.getSymbolIndex().?; - try self.writePtrWidthAtom(got_atom_index); - - self.markRelocsDirtyByTarget(atom.getSymbolWithLoc()); + try self.addGotEntry(.{ .sym_index = local_sym_index }); try self.writeAtom(atom_index, code); } @@ -1321,6 +1323,7 @@ fn updateDeclCode(self: *Coff, decl_index: Module.Decl.Index, code: []u8, comple const decl_metadata = self.decls.get(decl_index).?; const atom_index = decl_metadata.atom; const atom = self.getAtom(atom_index); + const sym_index = atom.getSymbolIndex().?; const sect_index = decl_metadata.section; const code_len = @intCast(u32, code.len); @@ -1340,10 +1343,8 @@ fn updateDeclCode(self: *Coff, decl_index: Module.Decl.Index, code: []u8, comple if (vaddr != sym.value) { sym.value = vaddr; log.debug(" (updating GOT entry)", .{}); - const got_target = SymbolWithLoc{ .sym_index = atom.getSymbolIndex().?, .file = null }; - const got_atom_index = self.getGotAtomIndexForSymbol(got_target).?; - self.markRelocsDirtyByTarget(got_target); - try self.writePtrWidthAtom(got_atom_index); + const got_entry_index = self.got_table.lookup.get(.{ .sym_index = sym_index }).?; + try self.writeOffsetTableEntry(got_entry_index); } } else if (code_len < atom.size) { self.shrinkAtom(atom_index, code_len); @@ -1361,15 +1362,9 @@ fn updateDeclCode(self: *Coff, decl_index: Module.Decl.Index, code: []u8, comple self.getAtomPtr(atom_index).size = code_len; sym.value = vaddr; - const got_target = SymbolWithLoc{ .sym_index = atom.getSymbolIndex().?, .file = null }; - const got_index = try self.allocateGotEntry(got_target); - const got_atom_index = try self.createGotAtom(got_target); - const got_atom = self.getAtom(got_atom_index); - self.got_entries.items[got_index].sym_index = got_atom.getSymbolIndex().?; - try self.writePtrWidthAtom(got_atom_index); + try self.addGotEntry(.{ .sym_index = sym_index }); } - self.markRelocsDirtyByTarget(atom.getSymbolWithLoc()); try self.writeAtom(atom_index, code); } @@ -1651,6 +1646,16 @@ pub fn flushModule(self: *Coff, comp: *Compilation, prog_node: *std.Progress.Nod try self.writeAtom(atom_index, code.items); } + // Update GOT if it got moved in memory. + if (self.got_table_contents_dirty) { + for (self.got_table.entries.items, 0..) |entry, i| { + if (!self.got_table.lookup.contains(entry)) continue; + // TODO: write all in one go rather than incrementally. + try self.writeOffsetTableEntry(i); + } + self.got_table_contents_dirty = false; + } + try self.writeBaseRelocations(); if (self.getEntryPoint()) |entry_sym_loc| { @@ -1752,10 +1757,10 @@ fn writeBaseRelocations(self: *Coff) !void { while (it.next()) |entry| { const atom_index = entry.key_ptr.*; const atom = self.getAtom(atom_index); + const sym = atom.getSymbol(self); const offsets = entry.value_ptr.*; for (offsets.items) |offset| { - const sym = atom.getSymbol(self); const rva = sym.value + offset; const page = mem.alignBackwardGeneric(u32, rva, self.page_size); const gop = try pages.getOrPut(page); @@ -1769,6 +1774,27 @@ fn writeBaseRelocations(self: *Coff) !void { } } + { + const header = &self.sections.items(.header)[self.got_section_index.?]; + for (self.got_table.entries.items, 0..) |entry, index| { + if (!self.got_table.lookup.contains(entry)) continue; + + const sym = self.getSymbol(entry); + if (sym.section_number == .UNDEFINED) continue; + + const rva = @intCast(u32, header.virtual_address + index * self.ptr_width.size()); + const page = mem.alignBackwardGeneric(u32, rva, self.page_size); + const gop = try pages.getOrPut(page); + if (!gop.found_existing) { + gop.value_ptr.* = std.ArrayList(coff.BaseRelocation).init(gpa); + } + try gop.value_ptr.append(.{ + .offset = @intCast(u12, rva - page), + .type = .DIR64, + }); + } + } + var buffer = std.ArrayList(u8).init(gpa); defer buffer.deinit(); @@ -2315,14 +2341,6 @@ pub fn getAtomIndexForSymbol(self: *const Coff, sym_loc: SymbolWithLoc) ?Atom.In return self.atom_by_index_table.get(sym_loc.sym_index); } -/// Returns GOT atom that references `sym_loc` if one exists. -/// Returns null otherwise. -pub fn getGotAtomIndexForSymbol(self: *const Coff, sym_loc: SymbolWithLoc) ?Atom.Index { - const got_index = self.got_entries_table.get(sym_loc) orelse return null; - const got_entry = self.got_entries.items[got_index]; - return self.getAtomIndexForSymbol(.{ .sym_index = got_entry.sym_index, .file = null }); -} - fn setSectionName(self: *Coff, header: *coff.SectionHeader, name: []const u8) !void { if (name.len <= 8) { mem.copy(u8, &header.name, name); @@ -2410,25 +2428,7 @@ fn logSymtab(self: *Coff) void { } log.debug("GOT entries:", .{}); - for (self.got_entries.items, 0..) |entry, i| { - const got_sym = self.getSymbol(.{ .sym_index = entry.sym_index, .file = null }); - const target_sym = self.getSymbol(entry.target); - if (target_sym.section_number == .UNDEFINED) { - log.debug(" {d}@{x} => import('{s}')", .{ - i, - got_sym.value, - self.getSymbolName(entry.target), - }); - } else { - log.debug(" {d}@{x} => local(%{d}) in object({?d}) {s}", .{ - i, - got_sym.value, - entry.target.sym_index, - entry.target.file, - logSymAttributes(target_sym, &buf), - }); - } - } + log.debug("{}", .{self.got_table}); } fn logSections(self: *Coff) void { @@ -2484,6 +2484,7 @@ const LlvmObject = @import("../codegen/llvm.zig").Object; const Module = @import("../Module.zig"); const Object = @import("Coff/Object.zig"); const Relocation = @import("Coff/Relocation.zig"); +const TableSection = @import("table_section.zig").TableSection; const StringTable = @import("strtab.zig").StringTable; const Type = @import("../type.zig").Type; const TypedValue = @import("../TypedValue.zig"); diff --git a/src/link/Coff/Relocation.zig b/src/link/Coff/Relocation.zig index 2fafa0bbdc..815f2cc7ee 100644 --- a/src/link/Coff/Relocation.zig +++ b/src/link/Coff/Relocation.zig @@ -48,17 +48,16 @@ dirty: bool = true, /// Returns address of the target if any. pub fn getTargetAddress(self: Relocation, coff_file: *const Coff) ?u32 { switch (self.type) { - .got, .got_page, .got_pageoff, .direct, .page, .pageoff => { - const maybe_target_atom_index = switch (self.type) { - .got, .got_page, .got_pageoff => coff_file.getGotAtomIndexForSymbol(self.target), - .direct, .page, .pageoff => coff_file.getAtomIndexForSymbol(self.target), - else => unreachable, - }; - const target_atom_index = maybe_target_atom_index orelse return null; + .got, .got_page, .got_pageoff => { + const got_index = coff_file.got_table.lookup.get(self.target) orelse return null; + const header = coff_file.sections.items(.header)[coff_file.got_section_index.?]; + return header.virtual_address + got_index * coff_file.ptr_width.size(); + }, + .direct, .page, .pageoff => { + const target_atom_index = coff_file.getAtomIndexForSymbol(self.target) orelse return null; const target_atom = coff_file.getAtom(target_atom_index); return target_atom.getSymbol(coff_file).value; }, - .import, .import_page, .import_pageoff => { const sym = coff_file.getSymbol(self.target); const index = coff_file.import_tables.getIndex(sym.value) orelse return null; -- cgit v1.2.3 From bee35fe3f018dd7b52716f757974ba405266558b Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Thu, 20 Apr 2023 14:07:02 +0200 Subject: coff: add image base to GOT relocations Sort base relocations by address for deterministic debugging. --- src/link/Coff.zig | 113 +++++++++++++++++++++++++++++------------------------- 1 file changed, 61 insertions(+), 52 deletions(-) (limited to 'src') diff --git a/src/link/Coff.zig b/src/link/Coff.zig index 00da6f02e0..b6def9564e 100644 --- a/src/link/Coff.zig +++ b/src/link/Coff.zig @@ -107,12 +107,6 @@ const HotUpdateState = struct { loaded_base_address: ?std.os.windows.HMODULE = null, }; -const Entry = struct { - target: SymbolWithLoc, - // Index into the synthetic symbol table (i.e., file == null). - sym_index: u32, -}; - const RelocTable = std.AutoArrayHashMapUnmanaged(Atom.Index, std.ArrayListUnmanaged(Relocation)); const BaseRelocationTable = std.AutoArrayHashMapUnmanaged(Atom.Index, std.ArrayListUnmanaged(u32)); const UnnamedConstTable = std.AutoArrayHashMapUnmanaged(Module.Decl.Index, std.ArrayListUnmanaged(Atom.Index)); @@ -843,17 +837,17 @@ fn writeOffsetTableEntry(self: *Coff, index: usize) !void { const file_offset = header.pointer_to_raw_data + entry_offset; const vmaddr = header.virtual_address + entry_offset; - log.debug("writing GOT entry {d}: @{x} => {x}", .{ index, vmaddr, entry_value }); + log.debug("writing GOT entry {d}: @{x} => {x}", .{ index, vmaddr, entry_value + self.getImageBase() }); switch (self.ptr_width) { .p32 => { var buf: [4]u8 = undefined; - mem.writeIntLittle(u32, &buf, @intCast(u32, entry_value)); + mem.writeIntLittle(u32, &buf, @intCast(u32, entry_value + self.getImageBase())); try self.base.file.?.pwriteAll(&buf, file_offset); }, .p64 => { var buf: [8]u8 = undefined; - mem.writeIntLittle(u64, &buf, entry_value); + mem.writeIntLittle(u64, &buf, entry_value + self.getImageBase()); try self.base.file.?.pwriteAll(&buf, file_offset); }, } @@ -881,12 +875,14 @@ fn writeOffsetTableEntry(self: *Coff, index: usize) !void { switch (self.ptr_width) { .p32 => { var buf: [4]u8 = undefined; + mem.writeIntLittle(u32, &buf, @intCast(u32, entry_value + slide)); writeMem(handle, pvaddr, &buf) catch |err| { log.warn("writing to protected memory failed with error: {s}", .{@errorName(err)}); }; }, .p64 => { var buf: [8]u8 = undefined; + mem.writeIntLittle(u64, &buf, entry_value + slide); writeMem(handle, pvaddr, &buf) catch |err| { log.warn("writing to protected memory failed with error: {s}", .{@errorName(err)}); }; @@ -1744,69 +1740,82 @@ pub fn updateDeclLineNumber(self: *Coff, module: *Module, decl_index: Module.Dec fn writeBaseRelocations(self: *Coff) !void { const gpa = self.base.allocator; - var pages = std.AutoHashMap(u32, std.ArrayList(coff.BaseRelocation)).init(gpa); + var page_table = std.AutoHashMap(u32, std.ArrayList(coff.BaseRelocation)).init(gpa); defer { - var it = pages.valueIterator(); + var it = page_table.valueIterator(); while (it.next()) |inner| { inner.deinit(); } - pages.deinit(); + page_table.deinit(); } - var it = self.base_relocs.iterator(); - while (it.next()) |entry| { - const atom_index = entry.key_ptr.*; - const atom = self.getAtom(atom_index); - const sym = atom.getSymbol(self); - const offsets = entry.value_ptr.*; - - for (offsets.items) |offset| { - const rva = sym.value + offset; - const page = mem.alignBackwardGeneric(u32, rva, self.page_size); - const gop = try pages.getOrPut(page); - if (!gop.found_existing) { - gop.value_ptr.* = std.ArrayList(coff.BaseRelocation).init(gpa); + { + var it = self.base_relocs.iterator(); + while (it.next()) |entry| { + const atom_index = entry.key_ptr.*; + const atom = self.getAtom(atom_index); + const sym = atom.getSymbol(self); + const offsets = entry.value_ptr.*; + + for (offsets.items) |offset| { + const rva = sym.value + offset; + const page = mem.alignBackwardGeneric(u32, rva, self.page_size); + const gop = try page_table.getOrPut(page); + if (!gop.found_existing) { + gop.value_ptr.* = std.ArrayList(coff.BaseRelocation).init(gpa); + } + try gop.value_ptr.append(.{ + .offset = @intCast(u12, rva - page), + .type = .DIR64, + }); } - try gop.value_ptr.append(.{ - .offset = @intCast(u12, rva - page), - .type = .DIR64, - }); } - } - { - const header = &self.sections.items(.header)[self.got_section_index.?]; - for (self.got_table.entries.items, 0..) |entry, index| { - if (!self.got_table.lookup.contains(entry)) continue; + { + const header = &self.sections.items(.header)[self.got_section_index.?]; + for (self.got_table.entries.items, 0..) |entry, index| { + if (!self.got_table.lookup.contains(entry)) continue; - const sym = self.getSymbol(entry); - if (sym.section_number == .UNDEFINED) continue; + const sym = self.getSymbol(entry); + if (sym.section_number == .UNDEFINED) continue; - const rva = @intCast(u32, header.virtual_address + index * self.ptr_width.size()); - const page = mem.alignBackwardGeneric(u32, rva, self.page_size); - const gop = try pages.getOrPut(page); - if (!gop.found_existing) { - gop.value_ptr.* = std.ArrayList(coff.BaseRelocation).init(gpa); + const rva = @intCast(u32, header.virtual_address + index * self.ptr_width.size()); + const page = mem.alignBackwardGeneric(u32, rva, self.page_size); + const gop = try page_table.getOrPut(page); + if (!gop.found_existing) { + gop.value_ptr.* = std.ArrayList(coff.BaseRelocation).init(gpa); + } + try gop.value_ptr.append(.{ + .offset = @intCast(u12, rva - page), + .type = .DIR64, + }); } - try gop.value_ptr.append(.{ - .offset = @intCast(u12, rva - page), - .type = .DIR64, - }); } } + // Sort pages by address. + var pages = try std.ArrayList(u32).initCapacity(gpa, page_table.count()); + defer pages.deinit(); + { + var it = page_table.keyIterator(); + while (it.next()) |page| { + pages.appendAssumeCapacity(page.*); + } + } + std.sort.sort(u32, pages.items, {}, std.sort.asc(u32)); + var buffer = std.ArrayList(u8).init(gpa); defer buffer.deinit(); - var pages_it = pages.iterator(); - while (pages_it.next()) |entry| { + for (pages.items) |page| { + const entries = page_table.getPtr(page).?; // Pad to required 4byte alignment if (!mem.isAlignedGeneric( usize, - entry.value_ptr.items.len * @sizeOf(coff.BaseRelocation), + entries.items.len * @sizeOf(coff.BaseRelocation), @sizeOf(u32), )) { - try entry.value_ptr.append(.{ + try entries.append(.{ .offset = 0, .type = .ABSOLUTE, }); @@ -1814,14 +1823,14 @@ fn writeBaseRelocations(self: *Coff) !void { const block_size = @intCast( u32, - entry.value_ptr.items.len * @sizeOf(coff.BaseRelocation) + @sizeOf(coff.BaseRelocationDirectoryEntry), + entries.items.len * @sizeOf(coff.BaseRelocation) + @sizeOf(coff.BaseRelocationDirectoryEntry), ); try buffer.ensureUnusedCapacity(block_size); buffer.appendSliceAssumeCapacity(mem.asBytes(&coff.BaseRelocationDirectoryEntry{ - .page_rva = entry.key_ptr.*, + .page_rva = page, .block_size = block_size, })); - buffer.appendSliceAssumeCapacity(mem.sliceAsBytes(entry.value_ptr.items)); + buffer.appendSliceAssumeCapacity(mem.sliceAsBytes(entries.items)); } const header = &self.sections.items(.header)[self.reloc_section_index.?]; -- cgit v1.2.3 From dd300d92e1c0812520ceffff4bc49529268efbff Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Thu, 20 Apr 2023 14:42:37 +0200 Subject: coff: treat vmaddr of 0 as unallocated when checking if resolvable --- src/link/Coff.zig | 2 +- src/link/Coff/Relocation.zig | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/link/Coff.zig b/src/link/Coff.zig index b6def9564e..50b98c2433 100644 --- a/src/link/Coff.zig +++ b/src/link/Coff.zig @@ -492,8 +492,8 @@ fn growSection(self: *Coff, sect_id: u32, needed_size: u32) !void { const sect_vm_capacity = self.allocatedVirtualSize(header.virtual_address); if (needed_size > sect_vm_capacity) { - try self.growSectionVirtualMemory(sect_id, needed_size); self.markRelocsDirtyByAddress(header.virtual_address + needed_size); + try self.growSectionVirtualMemory(sect_id, needed_size); } header.virtual_size = @max(header.virtual_size, needed_size); diff --git a/src/link/Coff/Relocation.zig b/src/link/Coff/Relocation.zig index 815f2cc7ee..4449691ac0 100644 --- a/src/link/Coff/Relocation.zig +++ b/src/link/Coff/Relocation.zig @@ -73,7 +73,8 @@ pub fn getTargetAddress(self: Relocation, coff_file: *const Coff) ?u32 { /// Returns true if and only if the reloc is dirty AND the target address is available. pub fn isResolvable(self: Relocation, coff_file: *Coff) bool { - _ = self.getTargetAddress(coff_file) orelse return false; + const addr = self.getTargetAddress(coff_file) orelse return false; + if (addr == 0) return false; return self.dirty; } -- cgit v1.2.3 From a596e48d8fa2ca6ae267dbb49458fe107c89b953 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Thu, 20 Apr 2023 18:28:00 +0200 Subject: coff: mark relocs dirty by target when updating GOT --- src/link/Coff.zig | 2 ++ 1 file changed, 2 insertions(+) (limited to 'src') diff --git a/src/link/Coff.zig b/src/link/Coff.zig index 50b98c2433..ac98457360 100644 --- a/src/link/Coff.zig +++ b/src/link/Coff.zig @@ -697,6 +697,7 @@ fn addGotEntry(self: *Coff, target: SymbolWithLoc) !void { const got_index = try self.got_table.allocateEntry(self.base.allocator, target); try self.writeOffsetTableEntry(got_index); self.got_table_count_dirty = true; + self.markRelocsDirtyByTarget(target); } pub fn createAtom(self: *Coff) !Atom.Index { @@ -1341,6 +1342,7 @@ fn updateDeclCode(self: *Coff, decl_index: Module.Decl.Index, code: []u8, comple log.debug(" (updating GOT entry)", .{}); const got_entry_index = self.got_table.lookup.get(.{ .sym_index = sym_index }).?; try self.writeOffsetTableEntry(got_entry_index); + self.markRelocsDirtyByTarget(.{ .sym_index = sym_index }); } } else if (code_len < atom.size) { self.shrinkAtom(atom_index, code_len); -- cgit v1.2.3 From 9a181f367d51acc653379c173b7661f9c69e5ad4 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Thu, 20 Apr 2023 18:28:18 +0200 Subject: macho: mark relocs dirty by target when updating GOT/stubs --- src/link/MachO.zig | 3 +++ 1 file changed, 3 insertions(+) (limited to 'src') diff --git a/src/link/MachO.zig b/src/link/MachO.zig index 4a3b76dd27..8975f38570 100644 --- a/src/link/MachO.zig +++ b/src/link/MachO.zig @@ -1801,6 +1801,7 @@ fn addGotEntry(self: *MachO, target: SymbolWithLoc) !void { const got_index = try self.got_table.allocateEntry(self.base.allocator, target); try self.writeOffsetTableEntry(got_index); self.got_table_count_dirty = true; + self.markRelocsDirtyByTarget(target); } fn addStubEntry(self: *MachO, target: SymbolWithLoc) !void { @@ -1808,6 +1809,7 @@ fn addStubEntry(self: *MachO, target: SymbolWithLoc) !void { const stub_index = try self.stub_table.allocateEntry(self.base.allocator, target); try self.writeStubTableEntry(stub_index); self.stub_table_count_dirty = true; + self.markRelocsDirtyByTarget(target); } pub fn updateFunc(self: *MachO, module: *Module, func: *Module.Fn, air: Air, liveness: Liveness) !void { @@ -2294,6 +2296,7 @@ fn updateDeclCode(self: *MachO, decl_index: Module.Decl.Index, code: []u8) !u64 log.debug(" (updating GOT entry)", .{}); const got_atom_index = self.got_table.lookup.get(.{ .sym_index = sym_index }).?; try self.writeOffsetTableEntry(got_atom_index); + self.markRelocsDirtyByTarget(.{ .sym_index = sym_index }); } } else if (code_len < atom.size) { self.shrinkAtom(atom_index, code_len); -- cgit v1.2.3 From 14dfbbc21365131c7ac85f08f543058f43fca0c2 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Fri, 21 Apr 2023 14:00:45 +0200 Subject: macho: rebase lazy pointers and apply corrected base offset --- src/link/MachO.zig | 49 +++++++++++++++++++++++++++++-------------------- 1 file changed, 29 insertions(+), 20 deletions(-) (limited to 'src') diff --git a/src/link/MachO.zig b/src/link/MachO.zig index 8975f38570..47954f8871 100644 --- a/src/link/MachO.zig +++ b/src/link/MachO.zig @@ -3000,6 +3000,28 @@ fn writeLinkeditSegmentData(self: *MachO) !void { seg.vmsize = mem.alignForwardGeneric(u64, seg.filesize, self.page_size); } +fn collectRebaseDataFromTableSection(self: *MachO, sect_id: u8, rebase: *Rebase, table: anytype) !void { + const header = self.sections.items(.header)[sect_id]; + const segment_index = self.sections.items(.segment_index)[sect_id]; + const segment = self.segments.items[segment_index]; + const base_offset = header.addr - segment.vmaddr; + const is_got = if (self.got_section_index) |index| index == sect_id else false; + + try rebase.entries.ensureUnusedCapacity(self.base.allocator, table.entries.items.len); + + for (table.entries.items, 0..) |entry, i| { + if (!table.lookup.contains(entry)) continue; + const sym = self.getSymbol(entry); + if (is_got and sym.undf()) continue; + const offset = i * @sizeOf(u64); + log.debug(" | rebase at {x}", .{base_offset + offset}); + rebase.entries.appendAssumeCapacity(.{ + .offset = base_offset + offset, + .segment_id = segment_index, + }); + } +} + fn collectRebaseData(self: *MachO, rebase: *Rebase) !void { const gpa = self.base.allocator; const slice = self.sections.slice(); @@ -3027,49 +3049,36 @@ fn collectRebaseData(self: *MachO, rebase: *Rebase) !void { } } - // Gather GOT pointers - try rebase.entries.ensureUnusedCapacity(gpa, self.got_table.entries.items.len); - 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 self.collectRebaseDataFromTableSection(self.got_section_index.?, rebase, self.got_table); + try self.collectRebaseDataFromTableSection(self.la_symbol_ptr_section_index.?, rebase, self.stub_table); try rebase.finalize(gpa); } fn collectBindDataFromTableSection(self: *MachO, sect_id: u8, bind: anytype, table: anytype) !void { + const header = self.sections.items(.header)[sect_id]; const segment_index = self.sections.items(.segment_index)[sect_id]; + const segment = self.segments.items[segment_index]; + const base_offset = header.addr - segment.vmaddr; try bind.entries.ensureUnusedCapacity(self.base.allocator, table.entries.items.len); for (table.entries.items, 0..) |entry, i| { if (!table.lookup.contains(entry)) continue; - const bind_sym = self.getSymbol(entry); if (!bind_sym.undf()) continue; - const offset = i * @sizeOf(u64); - log.debug(" | bind at {x}, import('{s}') in dylib({d})", .{ - offset, + base_offset + offset, self.getSymbolName(entry), @divTrunc(@bitCast(i16, bind_sym.n_desc), macho.N_SYMBOL_RESOLVER), }); if (bind_sym.weakRef()) { log.debug(" | marking as weak ref ", .{}); } - bind.entries.appendAssumeCapacity(.{ .target = entry, - .offset = offset, + .offset = base_offset + offset, .segment_id = segment_index, .addend = 0, }); -- cgit v1.2.3