From a112241f6454b3dd5b93703337339d38c2707be7 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Fri, 19 Jan 2024 20:28:05 +0100 Subject: [PATCH] macho: re-read atom code from ZigObject when resolving relocs --- src/link/MachO.zig | 73 +++++++++++++++++++++++++++++++++- src/link/MachO/Atom.zig | 11 +---- src/link/MachO/ZigObject.zig | 20 ++++++++-- src/link/MachO/relocatable.zig | 2 +- 4 files changed, 90 insertions(+), 16 deletions(-) diff --git a/src/link/MachO.zig b/src/link/MachO.zig index f4ba60a9df..36e34d5ff0 100644 --- a/src/link/MachO.zig +++ b/src/link/MachO.zig @@ -596,6 +596,47 @@ pub fn flushModule(self: *MachO, arena: Allocator, prog_node: *std.Progress.Node state_log.debug("{}", .{self.dumpState()}); try self.initDyldInfoSections(); + + // Beyond this point, everything has been allocated a virtual address and we can resolve + // the relocations, and commit objects to file. + if (self.getZigObject()) |zo| { + var has_resolve_error = false; + + for (zo.atoms.items) |atom_index| { + const atom = self.getAtom(atom_index) orelse continue; + if (!atom.flags.alive) continue; + const sect = &self.sections.items(.header)[atom.out_n_sect]; + if (sect.isZerofill()) continue; + const code = zo.getAtomDataAlloc(self, atom.*) catch |err| switch (err) { + error.InputOutput => { + try self.reportUnexpectedError("fetching code for '{s}' failed", .{ + atom.getName(self), + }); + return error.FlushFailure; + }, + else => |e| { + try self.reportUnexpectedError("unexpected error while fetching code for '{s}': {s}", .{ + atom.getName(self), + @errorName(e), + }); + return error.FlushFailure; + }, + }; + defer gpa.free(code); + const file_offset = sect.offset + atom.value - sect.addr; + atom.resolveRelocs(self, code) catch |err| switch (err) { + error.ResolveFailed => has_resolve_error = true, + else => |e| { + try self.reportUnexpectedError("unexpected error while resolving relocations", .{}); + return e; + }, + }; + try self.base.file.?.pwriteAll(code, file_offset); + } + + if (has_resolve_error) return error.FlushFailure; + } + self.writeAtoms() catch |err| switch (err) { error.ResolveFailed => return error.FlushFailure, else => |e| { @@ -1955,6 +1996,28 @@ pub fn sortSections(self: *MachO) !void { self.sections.appendAssumeCapacity(slice.get(sorted.index)); } + if (self.getZigObject()) |zo| { + for (zo.atoms.items) |atom_index| { + const atom = self.getAtom(atom_index) orelse continue; + if (!atom.flags.alive) continue; + atom.out_n_sect = backlinks[atom.out_n_sect]; + } + + for (zo.symtab.items(.nlist)) |*sym| { + if (sym.sect()) { + sym.n_sect = backlinks[sym.n_sect]; + } + } + + for (zo.symbols.items) |sym_index| { + const sym = self.getSymbol(sym_index); + const atom = sym.getAtom(self) orelse continue; + if (!atom.flags.alive) continue; + if (sym.getFile(self).?.getIndex() != zo.index) continue; + sym.out_n_sect = backlinks[sym.out_n_sect]; + } + } + for (self.objects.items) |index| { for (self.getFile(index).?.object.atoms.items) |atom_index| { const atom = self.getAtom(atom_index) orelse continue; @@ -1962,6 +2025,7 @@ pub fn sortSections(self: *MachO) !void { atom.out_n_sect = backlinks[atom.out_n_sect]; } } + if (self.getInternalObject()) |object| { for (object.atoms.items) |atom_index| { const atom = self.getAtom(atom_index) orelse continue; @@ -2517,6 +2581,7 @@ fn writeAtoms(self: *MachO) !void { const atom = self.getAtom(atom_index).?; assert(atom.flags.alive); const off = atom.value - header.addr; + @memcpy(buffer[off..][0..atom.size], atom.getFile(self).object.getAtomData(atom.*)); atom.resolveRelocs(self, buffer[off..][0..atom.size]) catch |err| switch (err) { error.ResolveFailed => has_resolve_error = true, else => |e| return e, @@ -2757,7 +2822,8 @@ pub fn calcSymtabSize(self: *MachO) !void { var files = std.ArrayList(File.Index).init(gpa); defer files.deinit(); - try files.ensureTotalCapacityPrecise(self.objects.items.len + self.dylibs.items.len + 1); + try files.ensureTotalCapacityPrecise(self.objects.items.len + self.dylibs.items.len + 2); + if (self.zig_object) |index| files.appendAssumeCapacity(index); for (self.objects.items) |index| files.appendAssumeCapacity(index); for (self.dylibs.items) |index| files.appendAssumeCapacity(index); if (self.internal_object) |index| files.appendAssumeCapacity(index); @@ -2816,6 +2882,9 @@ pub fn writeSymtab(self: *MachO, off: u32) !u32 { try self.symtab.resize(gpa, cmd.nsyms); try self.strtab.ensureUnusedCapacity(gpa, cmd.strsize - 1); + if (self.getZigObject()) |zo| { + zo.writeSymtab(self); + } for (self.objects.items) |index| { self.getFile(index).?.writeSymtab(self); } @@ -3752,7 +3821,7 @@ fn reportDependencyError( try err.addNote(self, "a dependency of {}", .{self.getFile(parent).?.fmtPath()}); } -fn reportUnexpectedError(self: *MachO, comptime format: []const u8, args: anytype) error{OutOfMemory}!void { +pub fn reportUnexpectedError(self: *MachO, comptime format: []const u8, args: anytype) error{OutOfMemory}!void { var err = try self.addErrorWithNotes(1); try err.addMsg(self, format, args); try err.addNote(self, "please report this as a linker bug on https://github.com/ziglang/zig/issues/new/choose", .{}); diff --git a/src/link/MachO/Atom.zig b/src/link/MachO/Atom.zig index d76bea03e0..90a3e129c9 100644 --- a/src/link/MachO/Atom.zig +++ b/src/link/MachO/Atom.zig @@ -50,14 +50,6 @@ pub fn getFile(self: Atom, macho_file: *MachO) File { return macho_file.getFile(self.file).?; } -pub fn getData(self: Atom, macho_file: *MachO) []const u8 { - return switch (self.getFile(macho_file)) { - .zig_object => @panic("TODO Atom.getData"), - .object => |x| x.getAtomData(self), - else => unreachable, - }; -} - pub fn getRelocs(self: Atom, macho_file: *MachO) []const Relocation { return switch (self.getFile(macho_file)) { .zig_object => |x| x.getAtomRelocs(self), @@ -538,7 +530,6 @@ pub fn resolveRelocs(self: Atom, macho_file: *MachO, buffer: []u8) !void { const file = self.getFile(macho_file); const name = self.getName(macho_file); const relocs = self.getRelocs(macho_file); - @memcpy(buffer, self.getData(macho_file)); relocs_log.debug("{x}: {s}", .{ self.value, name }); @@ -1153,7 +1144,7 @@ const macho = std.macho; const math = std.math; const mem = std.mem; const log = std.log.scoped(.link); -const relocs_log = std.log.scoped(.relocs); +const relocs_log = std.log.scoped(.link_relocs); const std = @import("std"); const trace = @import("../../tracy.zig").trace; diff --git a/src/link/MachO/ZigObject.zig b/src/link/MachO/ZigObject.zig index f57a11d3a6..23673f2438 100644 --- a/src/link/MachO/ZigObject.zig +++ b/src/link/MachO/ZigObject.zig @@ -128,6 +128,20 @@ pub fn addAtom(self: *ZigObject, macho_file: *MachO) !Symbol.Index { return symbol_index; } +/// Caller owns the memory. +pub fn getAtomDataAlloc(self: ZigObject, macho_file: *MachO, atom: Atom) ![]u8 { + const gpa = macho_file.base.comp.gpa; + assert(atom.file == self.index); + const sect = macho_file.sections.items(.header)[atom.out_n_sect]; + const file_offset = sect.offset + atom.value - sect.addr; + const size = std.math.cast(usize, atom.size) orelse return error.Overflow; + const code = try gpa.alloc(u8, size); + errdefer gpa.free(code); + const amt = try macho_file.base.file.?.preadAll(code, file_offset); + if (amt != code.len) return error.InputOutput; + return code; +} + pub fn getAtomRelocs(self: *ZigObject, atom: Atom) []const Relocation { const relocs = self.relocs.items[atom.relocs.pos]; return relocs.items[0..atom.relocs.len]; @@ -659,7 +673,7 @@ fn updateDeclCode( if (old_size > 0) { const capacity = atom.capacity(macho_file); - const need_realloc = code.len > capacity or !required_alignment.check(sym.getAddress(.{}, macho_file)); + const need_realloc = code.len > capacity or !required_alignment.check(atom.value); if (need_realloc) { try atom.grow(macho_file); @@ -678,7 +692,7 @@ fn updateDeclCode( } else if (code.len < old_size) { atom.shrink(macho_file); } else if (macho_file.getAtom(atom.next_index) == null) { - const needed_size = (sym.getAddress(.{}, macho_file) + code.len) - sect.addr; + const needed_size = atom.value + code.len - sect.addr; sect.size = needed_size; } } else { @@ -696,7 +710,7 @@ fn updateDeclCode( } if (!sect.isZerofill()) { - const file_offset = sect.offset + sym.getAddress(.{}, macho_file) - sect.addr; + const file_offset = sect.offset + atom.value - sect.addr; try macho_file.base.file.?.pwriteAll(code, file_offset); } } diff --git a/src/link/MachO/relocatable.zig b/src/link/MachO/relocatable.zig index 323bf9d76f..8b47d8eeb5 100644 --- a/src/link/MachO/relocatable.zig +++ b/src/link/MachO/relocatable.zig @@ -274,7 +274,7 @@ fn writeAtoms(macho_file: *MachO) !void { const atom = macho_file.getAtom(atom_index).?; assert(atom.flags.alive); const off = atom.value - header.addr; - @memcpy(code[off..][0..atom.size], atom.getData(macho_file)); + @memcpy(code[off..][0..atom.size], atom.getFile(macho_file).object.getAtomData(atom.*)); try atom.writeRelocs(macho_file, code[off..][0..atom.size], &relocs); }