macho: introduce ZigObject

This commit is contained in:
Jakub Konka 2024-01-15 20:34:50 +01:00
parent 03b33b0f01
commit 6d0ba6dd10
8 changed files with 262 additions and 31 deletions

View File

@ -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"

View File

@ -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");

View File

@ -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,
}
}

View File

@ -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);

View File

@ -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}),

View File

@ -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();

View File

@ -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");

View File

@ -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);
}