diff --git a/CMakeLists.txt b/CMakeLists.txt index 851e55492..0942f9530 100644 --- a/CMakeLists.txt +++ b/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" diff --git a/src/link/MachO.zig b/src/link/MachO.zig index 8d959275a..956f06a49 100644 --- a/src/link/MachO.zig +++ b/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"); diff --git a/src/link/MachO/Atom.zig b/src/link/MachO/Atom.zig index ead0e96a5..3e9884f77 100644 --- a/src/link/MachO/Atom.zig +++ b/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, } } diff --git a/src/link/MachO/Object.zig b/src/link/MachO/Object.zig index 136b39d61..d31aaf667 100644 --- a/src/link/MachO/Object.zig +++ b/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); diff --git a/src/link/MachO/Symbol.zig b/src/link/MachO/Symbol.zig index 9355c0db2..c6cf3f963 100644 --- a/src/link/MachO/Symbol.zig +++ b/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}), diff --git a/src/link/MachO/ZigObject.zig b/src/link/MachO/ZigObject.zig new file mode 100644 index 000000000..e8adebc5c --- /dev/null +++ b/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(); diff --git a/src/link/MachO/file.zig b/src/link/MachO/file.zig index 9e19bed7d..7033f5876 100644 --- a/src/link/MachO/file.zig +++ b/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"); diff --git a/src/link/MachO/relocatable.zig b/src/link/MachO/relocatable.zig index 00513479b..323bf9d76 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.getCode(macho_file)); + @memcpy(code[off..][0..atom.size], atom.getData(macho_file)); try atom.writeRelocs(macho_file, code[off..][0..atom.size], &relocs); }