Commit 6d0ba6dd10

Jakub Konka <kubkon@jakubkonka.com>
2024-01-15 20:34:50
macho: introduce ZigObject
1 parent 03b33b0
src/link/MachO/Atom.zig
@@ -45,10 +45,27 @@ 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 => @panic("TODO Atom.getRelocs"),
+        .object => |x| x.getAtomRelocs(self),
+        else => unreachable,
+    };
+}
+
 pub fn getInputSection(self: Atom, macho_file: *MachO) macho.section_64 {
     return switch (self.getFile(macho_file)) {
-        .dylib => unreachable,
-        inline else => |x| x.sections.items(.header)[self.n_sect],
+        .zig_object => |x| x.getInputSection(self, macho_file),
+        .object => |x| x.sections.items(.header)[self.n_sect],
+        else => unreachable,
     };
 }
 
@@ -61,26 +78,10 @@ pub fn getPriority(self: Atom, macho_file: *MachO) u64 {
     return (@as(u64, @intCast(file.getIndex())) << 32) | @as(u64, @intCast(self.n_sect));
 }
 
-pub fn getCode(self: Atom, macho_file: *MachO) []const u8 {
-    const code = switch (self.getFile(macho_file)) {
-        .dylib => unreachable,
-        inline else => |x| x.getSectionData(self.n_sect),
-    };
-    return code[self.off..][0..self.size];
-}
-
-pub fn getRelocs(self: Atom, macho_file: *MachO) []const Relocation {
-    const relocs = switch (self.getFile(macho_file)) {
-        .dylib => unreachable,
-        inline else => |x| x.sections.items(.relocs)[self.n_sect],
-    };
-    return relocs.items[self.relocs.pos..][0..self.relocs.len];
-}
-
 pub fn getUnwindRecords(self: Atom, macho_file: *MachO) []const UnwindInfo.Record.Index {
     return switch (self.getFile(macho_file)) {
         .dylib => unreachable,
-        .internal => &[0]UnwindInfo.Record.Index{},
+        .zig_object, .internal => &[0]UnwindInfo.Record.Index{},
         .object => |x| x.unwind_records.items[self.unwind_records.pos..][0..self.unwind_records.len],
     };
 }
@@ -290,10 +291,10 @@ pub fn resolveRelocs(self: Atom, macho_file: *MachO, buffer: []u8) !void {
     defer tracy.end();
 
     assert(!self.getInputSection(macho_file).isZerofill());
-    const relocs = self.getRelocs(macho_file);
     const file = self.getFile(macho_file);
     const name = self.getName(macho_file);
-    @memcpy(buffer, self.getCode(macho_file));
+    const relocs = self.getRelocs(macho_file);
+    @memcpy(buffer, self.getData(macho_file));
 
     relocs_log.debug("{x}: {s}", .{ self.value, name });
 
@@ -683,10 +684,11 @@ const x86_64 = struct {
 };
 
 pub fn calcNumRelocs(self: Atom, macho_file: *MachO) u32 {
+    const relocs = self.getRelocs(macho_file);
     switch (macho_file.getTarget().cpu.arch) {
         .aarch64 => {
             var nreloc: u32 = 0;
-            for (self.getRelocs(macho_file)) |rel| {
+            for (relocs) |rel| {
                 nreloc += 1;
                 switch (rel.type) {
                     .page, .pageoff => if (rel.addend > 0) {
@@ -697,7 +699,7 @@ pub fn calcNumRelocs(self: Atom, macho_file: *MachO) u32 {
             }
             return nreloc;
         },
-        .x86_64 => return @intCast(self.getRelocs(macho_file).len),
+        .x86_64 => return @intCast(relocs.len),
         else => unreachable,
     }
 }
src/link/MachO/file.zig
@@ -1,4 +1,5 @@
 pub const File = union(enum) {
+    zig_object: *ZigObject,
     internal: *InternalObject,
     object: *Object,
     dylib: *Dylib,
@@ -22,6 +23,7 @@ pub const File = union(enum) {
         _ = unused_fmt_string;
         _ = options;
         switch (file) {
+            .zig_object => |x| try writer.writeAll(x.path),
             .internal => try writer.writeAll(""),
             .object => |x| try writer.print("{}", .{x.fmtPath()}),
             .dylib => |x| try writer.writeAll(x.path),
@@ -98,6 +100,7 @@ pub const File = union(enum) {
 
     pub const Entry = union(enum) {
         null: void,
+        zig_object: ZigObject,
         internal: InternalObject,
         object: Object,
         dylib: Dylib,
@@ -114,3 +117,4 @@ const MachO = @import("../MachO.zig");
 const Object = @import("Object.zig");
 const Dylib = @import("Dylib.zig");
 const Symbol = @import("Symbol.zig");
+const ZigObject = @import("ZigObject.zig");
src/link/MachO/Object.zig
@@ -1547,6 +1547,16 @@ pub fn getSectionData(self: *const Object, index: u32) []const u8 {
     return self.data[sect.offset..][0..sect.size];
 }
 
+pub fn getAtomData(self: *const Object, atom: Atom) []const u8 {
+    const data = self.getSectionData(atom.n_sect);
+    return data[atom.off..][0..atom.size];
+}
+
+pub fn getAtomRelocs(self: *const Object, atom: Atom) []const Relocation {
+    const relocs = self.sections.items(.relocs)[atom.n_sect];
+    return relocs.items[atom.relocs.pos..][0..atom.relocs.len];
+}
+
 fn getString(self: Object, off: u32) [:0]const u8 {
     assert(off < self.strtab.len);
     return mem.sliceTo(@as([*:0]const u8, @ptrCast(self.strtab.ptr + off)), 0);
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.getCode(macho_file));
+            @memcpy(code[off..][0..atom.size], atom.getData(macho_file));
             try atom.writeRelocs(macho_file, code[off..][0..atom.size], &relocs);
         }
 
src/link/MachO/Symbol.zig
@@ -306,6 +306,7 @@ fn format2(
         if (symbol.flags.weak) try writer.writeAll(" : weak");
         if (symbol.isSymbolStab(ctx.macho_file)) try writer.writeAll(" : stab");
         switch (file) {
+            .zig_object => |x| try writer.print(" : zig_object({d})", .{x.index}),
             .internal => |x| try writer.print(" : internal({d})", .{x.index}),
             .object => |x| try writer.print(" : object({d})", .{x.index}),
             .dylib => |x| try writer.print(" : dylib({d})", .{x.index}),
src/link/MachO/ZigObject.zig
@@ -0,0 +1,199 @@
+/// Externally owned memory.
+path: []const u8,
+index: File.Index,
+
+symtab: std.MultiArrayList(Nlist) = .{},
+
+symbols: std.ArrayListUnmanaged(Symbol.Index) = .{},
+atoms: std.ArrayListUnmanaged(Atom.Index) = .{},
+
+output_symtab_ctx: MachO.SymtabCtx = .{},
+
+pub fn init(self: *ZigObject, macho_file: *MachO) !void {
+    const comp = macho_file.base.comp;
+    const gpa = comp.gpa;
+
+    try self.atoms.append(gpa, 0); // null input section
+}
+
+pub fn deinit(self: *ZigObject, allocator: Allocator) void {
+    self.symtab.deinit(allocator);
+    self.symbols.deinit(allocator);
+    self.atoms.deinit(allocator);
+}
+
+fn addNlist(self: *ZigObject, allocator: Allocator) !Symbol.Index {
+    try self.symtab.ensureUnusedCapacity(allocator, 1);
+    const index = @as(Symbol.Index, @intCast(self.symtab.addOneAssumeCapacity()));
+    self.symtab.set(index, .{
+        .nlist = MachO.null_sym,
+        .size = 0,
+        .atom = 0,
+    });
+    return index;
+}
+
+pub fn getDeclVAddr(
+    self: *ZigObject,
+    macho_file: *MachO,
+    decl_index: InternPool.DeclIndex,
+    reloc_info: link.File.RelocInfo,
+) !u64 {
+    _ = self;
+    _ = macho_file;
+    _ = decl_index;
+    _ = reloc_info;
+    @panic("TODO getDeclVAddr");
+}
+
+pub fn resolveSymbols(self: *ZigObject, macho_file: *MachO) void {
+    _ = self;
+    _ = macho_file;
+    @panic("TODO resolveSymbols");
+}
+
+pub fn resetGlobals(self: *ZigObject, macho_file: *MachO) void {
+    for (self.symbols.items, 0..) |sym_index, nlist_idx| {
+        if (!self.symtab.items(.nlist)[nlist_idx].ext()) continue;
+        const sym = macho_file.getSymbol(sym_index);
+        const name = sym.name;
+        sym.* = .{};
+        sym.name = name;
+    }
+}
+
+pub fn calcSymtabSize(self: *ZigObject, macho_file: *MachO) !void {
+    const tracy = trace(@src());
+    defer tracy.end();
+
+    for (self.symbols.items) |sym_index| {
+        const sym = macho_file.getSymbol(sym_index);
+        const file = sym.getFile(macho_file) orelse continue;
+        if (file.getIndex() != self.index) continue;
+        if (sym.getAtom(macho_file)) |atom| if (!atom.flags.alive) continue;
+        sym.flags.output_symtab = true;
+        if (sym.isLocal()) {
+            try sym.addExtra(.{ .symtab = self.output_symtab_ctx.nlocals }, macho_file);
+            self.output_symtab_ctx.nlocals += 1;
+        } else if (sym.flags.@"export") {
+            try sym.addExtra(.{ .symtab = self.output_symtab_ctx.nexports }, macho_file);
+            self.output_symtab_ctx.nexports += 1;
+        } else {
+            assert(sym.flags.import);
+            try sym.addExtra(.{ .symtab = self.output_symtab_ctx.nimports }, macho_file);
+            self.output_symtab_ctx.nimports += 1;
+        }
+        self.output_symtab_ctx.strsize += @as(u32, @intCast(sym.getName(macho_file).len + 1));
+    }
+}
+
+pub fn writeSymtab(self: ZigObject, macho_file: *MachO) void {
+    const tracy = trace(@src());
+    defer tracy.end();
+
+    for (self.symbols.items) |sym_index| {
+        const sym = macho_file.getSymbol(sym_index);
+        const file = sym.getFile(macho_file) orelse continue;
+        if (file.getIndex() != self.index) continue;
+        const idx = sym.getOutputSymtabIndex(macho_file) orelse continue;
+        const n_strx = @as(u32, @intCast(macho_file.strtab.items.len));
+        macho_file.strtab.appendSliceAssumeCapacity(sym.getName(macho_file));
+        macho_file.strtab.appendAssumeCapacity(0);
+        const out_sym = &macho_file.symtab.items[idx];
+        out_sym.n_strx = n_strx;
+        sym.setOutputSym(macho_file, out_sym);
+    }
+}
+
+pub fn getInputSection(self: ZigObject, atom: Atom, macho_file: *MachO) macho.section_64 {
+    _ = self;
+    var sect = macho_file.sections.items(.header)[atom.out_n_sect];
+    sect.addr = 0;
+    sect.offset = 0;
+    sect.size = atom.size;
+    sect.@"align" = atom.alignment.toLog2Units();
+    return sect;
+}
+
+pub fn fmtSymtab(self: *ZigObject, macho_file: *MachO) std.fmt.Formatter(formatSymtab) {
+    return .{ .data = .{
+        .self = self,
+        .macho_file = macho_file,
+    } };
+}
+
+const FormatContext = struct {
+    self: *ZigObject,
+    macho_file: *MachO,
+};
+
+fn formatSymtab(
+    ctx: FormatContext,
+    comptime unused_fmt_string: []const u8,
+    options: std.fmt.FormatOptions,
+    writer: anytype,
+) !void {
+    _ = unused_fmt_string;
+    _ = options;
+    try writer.writeAll("  symbols\n");
+    for (ctx.self.symbols.items) |index| {
+        const sym = ctx.macho_file.getSymbol(index);
+        try writer.print("    {}\n", .{sym.fmt(ctx.macho_file)});
+    }
+}
+
+pub fn fmtAtoms(self: *ZigObject, macho_file: *MachO) std.fmt.Formatter(formatAtoms) {
+    return .{ .data = .{
+        .self = self,
+        .macho_file = macho_file,
+    } };
+}
+
+fn formatAtoms(
+    ctx: FormatContext,
+    comptime unused_fmt_string: []const u8,
+    options: std.fmt.FormatOptions,
+    writer: anytype,
+) !void {
+    _ = unused_fmt_string;
+    _ = options;
+    try writer.writeAll("  atoms\n");
+    for (ctx.self.atoms.items) |atom_index| {
+        const atom = ctx.macho_file.getAtom(atom_index) orelse continue;
+        try writer.print("    {}\n", .{atom.fmt(ctx.macho_file)});
+    }
+}
+
+const Nlist = struct {
+    nlist: macho.nlist_64,
+    size: u64,
+    atom: Atom.Index,
+};
+
+const assert = std.debug.assert;
+const builtin = @import("builtin");
+const codegen = @import("../../codegen.zig");
+const link = @import("../../link.zig");
+const log = std.log.scoped(.link);
+const macho = std.macho;
+const mem = std.mem;
+const trace = @import("../../tracy.zig").trace;
+const std = @import("std");
+
+const Air = @import("../../Air.zig");
+const Allocator = std.mem.Allocator;
+const Archive = @import("Archive.zig");
+const Atom = @import("Atom.zig");
+const Dwarf = @import("../Dwarf.zig");
+const File = @import("file.zig").File;
+const InternPool = @import("../../InternPool.zig");
+const Liveness = @import("../../Liveness.zig");
+const MachO = @import("../MachO.zig");
+const Module = @import("../../Module.zig");
+const Object = @import("Object.zig");
+const Symbol = @import("Symbol.zig");
+const StringTable = @import("../StringTable.zig");
+const Type = @import("../../type.zig").Type;
+const Value = @import("../../value.zig").Value;
+const TypedValue = @import("../../TypedValue.zig");
+const ZigObject = @This();
src/link/MachO.zig
@@ -10,6 +10,7 @@ d_sym: ?DebugSymbols = null,
 /// Index of each input file also encodes the priority or precedence of one input file
 /// over another.
 files: std.MultiArrayList(File.Entry) = .{},
+zig_object: ?File.Index = null,
 internal_object: ?File.Index = null,
 objects: std.ArrayListUnmanaged(File.Index) = .{},
 dylibs: std.ArrayListUnmanaged(File.Index) = .{},
@@ -222,12 +223,19 @@ pub fn createEmpty(
     try self.symbols.append(gpa, .{});
     try self.symbols_extra.append(gpa, 0);
 
-    // TODO: init
-
     if (opt_zcu) |zcu| {
         if (!use_llvm) {
-            _ = zcu;
-            // TODO: create .zig_object
+            const index: File.Index = @intCast(try self.files.addOne(gpa));
+            self.files.set(index, .{ .zig_object = .{
+                .index = index,
+                .path = try std.fmt.allocPrint(arena, "{s}.o", .{std.fs.path.stem(
+                    zcu.main_mod.root_src_path,
+                )}),
+            } });
+            self.zig_object = index;
+            try self.getZigObject().?.init(self);
+
+            // TODO init metadata
 
             if (comp.config.debug_format != .strip) {
                 // Create dSYM bundle.
@@ -281,6 +289,7 @@ pub fn deinit(self: *MachO) void {
 
     for (self.files.items(.tags), self.files.items(.data)) |tag, *data| switch (tag) {
         .null => {},
+        .zig_object => data.zig_object.deinit(gpa),
         .internal => data.internal.deinit(gpa),
         .object => data.object.deinit(gpa),
         .dylib => data.dylib.deinit(gpa),
@@ -3109,9 +3118,7 @@ pub fn freeDecl(self: *MachO, decl_index: InternPool.DeclIndex) void {
 
 pub fn getDeclVAddr(self: *MachO, decl_index: InternPool.DeclIndex, reloc_info: link.File.RelocInfo) !u64 {
     assert(self.llvm_object == null);
-    _ = decl_index;
-    _ = reloc_info;
-    @panic("TODO getDeclVAddr");
+    return self.getZigObject().?.getDeclVAddr(self, decl_index, reloc_info);
 }
 
 pub fn lowerAnonDecl(
@@ -3297,12 +3304,18 @@ pub fn getFile(self: *MachO, index: File.Index) ?File {
     const tag = self.files.items(.tags)[index];
     return switch (tag) {
         .null => null,
+        .zig_object => .{ .zig_object = &self.files.items(.data)[index].zig_object },
         .internal => .{ .internal = &self.files.items(.data)[index].internal },
         .object => .{ .object = &self.files.items(.data)[index].object },
         .dylib => .{ .dylib = &self.files.items(.data)[index].dylib },
     };
 }
 
+pub fn getZigObject(self: *MachO) ?*ZigObject {
+    const index = self.zig_object orelse return null;
+    return self.getFile(index).?.zig_object;
+}
+
 pub fn getInternalObject(self: *MachO) ?*InternalObject {
     const index = self.internal_object orelse return null;
     return self.getFile(index).?.internal;
@@ -4123,3 +4136,4 @@ const TlvPtrSection = synthetic.TlvPtrSection;
 const TypedValue = @import("../TypedValue.zig");
 const UnwindInfo = @import("MachO/UnwindInfo.zig");
 const WeakBindSection = synthetic.WeakBindSection;
+const ZigObject = @import("MachO/ZigObject.zig");
CMakeLists.txt
@@ -608,6 +608,7 @@ set(ZIG_STAGE2_SOURCES
     "${CMAKE_SOURCE_DIR}/src/link/MachO/Relocation.zig"
     "${CMAKE_SOURCE_DIR}/src/link/MachO/Symbol.zig"
     "${CMAKE_SOURCE_DIR}/src/link/MachO/UnwindInfo.zig"
+    "${CMAKE_SOURCE_DIR}/src/link/MachO/ZigObject.zig"
     "${CMAKE_SOURCE_DIR}/src/link/MachO/dead_strip.zig"
     "${CMAKE_SOURCE_DIR}/src/link/MachO/dyld_info/bind.zig"
     "${CMAKE_SOURCE_DIR}/src/link/MachO/dyld_info/Rebase.zig"