From 4019d010007294de58acd5370cc879f14d40e506 Mon Sep 17 00:00:00 2001 From: Matthew Lugg Date: Sun, 2 Aug 2026 16:11:55 +0100 Subject: [PATCH] MachO2: stub new linker implementation --- src/dev.zig | 2 + src/link.zig | 26 +++-- src/link/MachO2.zig | 275 ++++++++++++++++++++++++++++++++++++++++++++ src/target.zig | 2 +- 4 files changed, 295 insertions(+), 10 deletions(-) create mode 100644 src/link/MachO2.zig diff --git a/src/dev.zig b/src/dev.zig index 683824812a06bb09bb45a25bd43dcf7ecdb6f372..6472f65f518bcc909f72ce3863c72dd0c6a2c4fc 100644 --- a/src/dev.zig +++ b/src/dev.zig @@ -112,6 +112,7 @@ pub const Env = enum { .elf_linker, .elf2_linker, .macho_linker, + .macho2_linker, .c_linker, .wasm_linker, .spirv_linker, @@ -323,6 +324,7 @@ pub const Feature = enum { elf_linker, elf2_linker, macho_linker, + macho2_linker, c_linker, wasm_linker, spirv_linker, diff --git a/src/link.zig b/src/link.zig index c72a65a888eb4489153569c6971c9eec6d63f217..62751df5dd2d619f09764d5dd3aa47ba24aa9840 100644 --- a/src/link.zig +++ b/src/link.zig @@ -587,7 +587,7 @@ pub const File = struct { pub fn startProgress(base: *File, prog_node: std.Progress.Node) void { switch (base.tag) { else => {}, - inline .elf2, .coff => |tag| { + inline .elf2, .coff, .macho2 => |tag| { dev.check(tag.devFeature()); return @as(*tag.Type(), @fieldParentPtr("base", base)).startProgress(prog_node); }, @@ -597,7 +597,7 @@ pub const File = struct { pub fn endProgress(base: *File) void { switch (base.tag) { else => {}, - inline .elf2, .coff => |tag| { + inline .elf2, .coff, .macho2 => |tag| { dev.check(tag.devFeature()); return @as(*tag.Type(), @fieldParentPtr("base", base)).endProgress(); }, @@ -651,11 +651,13 @@ pub const File = struct { } base.file = try emit.root_dir.handle.openFile(io, emit.sub_path, .{ .mode = .read_write }); }, - .elf2, .coff => if (base.file == null) { + .elf2, .coff, .macho2 => if (base.file == null) { const mf = if (base.cast(.elf2)) |elf| &elf.mf else if (base.cast(.coff)) |coff| &coff.mf + else if (base.cast(.macho2)) |macho| + &macho.mf else unreachable; mf.memory_map.file = try base.emit.root_dir.handle.openFile(io, base.emit.sub_path, .{ @@ -740,11 +742,13 @@ pub const File = struct { } } }, - .elf2, .coff => if (base.file) |f| { + .elf2, .coff, .macho2 => if (base.file) |f| { const mf = if (base.cast(.elf2)) |elf| &elf.mf else if (base.cast(.coff)) |coff| &coff.mf + else if (base.cast(.macho2)) |macho| + &macho.mf else unreachable; mf.unmap(); @@ -900,6 +904,7 @@ pub const File = struct { .plan9 => unreachable, .spirv => {}, .coff => {}, + .macho2 => {}, inline else => |tag| { dev.check(tag.devFeature()); return @as(*tag.Type(), @fieldParentPtr("base", base)).updateLineNumber(pt, ti_id, line); @@ -951,7 +956,7 @@ pub const File = struct { pub fn idle(base: *File, tid: Zcu.PerThread.Id) Error!bool { switch (base.tag) { else => return false, - inline .elf2, .coff => |tag| { + inline .elf2, .coff, .macho2 => |tag| { dev.check(tag.devFeature()); return @as(*tag.Type(), @fieldParentPtr("base", base)).idle(tid); }, @@ -961,7 +966,7 @@ pub const File = struct { pub fn updateErrorData(base: *File, pt: Zcu.PerThread) Error!void { switch (base.tag) { else => {}, - inline .elf2, .coff => |tag| { + inline .elf2, .coff, .macho2 => |tag| { dev.check(tag.devFeature()); return @as(*tag.Type(), @fieldParentPtr("base", base)).updateErrorData(pt); }, @@ -1234,7 +1239,7 @@ pub const File = struct { assert(!base.post_prelink); switch (base.tag) { - inline .coff, .elf, .elf2, .wasm, .spirv => |tag| { + inline .coff, .elf, .elf2, .macho2, .wasm, .spirv => |tag| { dev.check(tag.devFeature()); return @as(*tag.Type(), @fieldParentPtr("base", base)).loadInput(input); }, @@ -1253,7 +1258,7 @@ pub const File = struct { } switch (base.tag) { - inline .elf2, .coff, .wasm, .c => |tag| { + inline .elf2, .coff, .macho2, .wasm, .c => |tag| { dev.check(tag.devFeature()); try @as(*tag.Type(), @fieldParentPtr("base", base)).prelink(base.comp.link_prog_node); }, @@ -1298,6 +1303,7 @@ pub const File = struct { elf, elf2, macho, + macho2, c, wasm, spirv, @@ -1311,6 +1317,7 @@ pub const File = struct { .elf => Elf, .elf2 => Elf2, .macho => MachO, + .macho2 => MachO2, .c => C, .wasm => Wasm, .spirv => Spirv, @@ -1324,7 +1331,7 @@ pub const File = struct { return switch (ofmt) { .coff => .coff, .elf => if (use_new_linker) .elf2 else .elf, - .macho => .macho, + .macho => if (use_new_linker) .macho2 else .macho, .wasm => .wasm, .plan9 => .plan9, .c => .c, @@ -1417,6 +1424,7 @@ pub const File = struct { pub const Elf = @import("link/Elf.zig"); pub const Elf2 = @import("link/Elf2.zig"); pub const MachO = @import("link/MachO.zig"); + pub const MachO2 = @import("link/MachO2.zig"); pub const Spirv = @import("link/Spirv.zig"); pub const Wasm = @import("link/Wasm.zig"); pub const Dwarf = @import("link/Dwarf.zig"); diff --git a/src/link/MachO2.zig b/src/link/MachO2.zig new file mode 100644 index 0000000000000000000000000000000000000000..c01ed3cf0b62d1a22555792236c32ffea0fb098d --- /dev/null +++ b/src/link/MachO2.zig @@ -0,0 +1,275 @@ +const MachO = @This(); + +base: link.File, +options: link.File.OpenOptions, +mf: MappedFile, +nodes: std.MultiArrayList(Node), + +const_prog_node: std.Progress.Node, +synth_prog_node: std.Progress.Node, +input_prog_node: std.Progress.Node, + +const Node = union(enum) { + macho, +}; +fn getNode(macho: *MachO, ni: MappedFile.Node.Index) Node { + return macho.nodes.get(@backingInt(ni)); +} + +pub fn open( + arena: std.mem.Allocator, + comp: *Compilation, + path: std.Build.Cache.Path, + options: link.File.OpenOptions, +) !*MachO { + return create(arena, comp, path, options); +} +pub fn createEmpty( + arena: std.mem.Allocator, + comp: *Compilation, + path: std.Build.Cache.Path, + options: link.File.OpenOptions, +) !*MachO { + return create(arena, comp, path, options); +} +fn create( + arena: std.mem.Allocator, + comp: *Compilation, + path: std.Build.Cache.Path, + options: link.File.OpenOptions, +) !*MachO { + const io = comp.io; + + const macho = try arena.create(MachO); + const file = try path.root_dir.handle.createFile(io, path.sub_path, .{ + .read = true, + .permissions = link.File.determinePermissions(comp.config.output_mode, comp.config.link_mode), + }); + + macho.* = .{ + .base = .{ + .tag = .macho2, + + .comp = comp, + .emit = path, + + .file = file, + .gc_sections = false, + .print_gc_sections = false, + .build_id = .none, + .allow_shlib_undefined = false, + .stack_size = 0, + }, + .options = options, + .mf = try .init(file, comp.gpa, io), + .nodes = .empty, + .const_prog_node = .none, + .synth_prog_node = .none, + .input_prog_node = .none, + }; + errdefer macho.deinit(); + + try macho.initInner(); + + return macho; +} +fn initInner(macho: *MachO) !void { + const comp = macho.base.comp; + const gpa = comp.gpa; + + try macho.nodes.append(gpa, .macho); // file root node + + @panic("TODO"); +} + +pub fn deinit(macho: *MachO) void { + const gpa = macho.base.comp.gpa; + macho.mf.deinit(gpa); + macho.nodes.deinit(gpa); + macho.* = undefined; +} + +pub fn startProgress(macho: *MachO, prog_node: std.Progress.Node) void { + prog_node.increaseEstimatedTotalItems(4); + macho.const_prog_node = prog_node.start("Constants", 0); + macho.synth_prog_node = prog_node.start("Synthetics", 0); + macho.input_prog_node = prog_node.start("Inputs", 0); + macho.mf.update_prog_node = prog_node.start("Relocations", macho.mf.updates.items.len); +} +pub fn endProgress(macho: *MachO) void { + macho.const_prog_node.end(); + macho.const_prog_node = .none; + macho.synth_prog_node.end(); + macho.synth_prog_node = .none; + macho.input_prog_node.end(); + macho.input_prog_node = .none; + macho.mf.update_prog_node.end(); + macho.mf.update_prog_node = .none; +} + +pub fn navSymbol(macho: *MachO, nav_id: InternPool.Nav.Index) link.Error!link.File.SymbolId { + _ = macho; + _ = nav_id; + @panic("TODO"); +} +pub fn uavSymbol( + macho: *MachO, + pt: Zcu.PerThread, + uav_val: InternPool.Index, + uav_align: InternPool.Alignment, +) link.Error!link.File.SymbolId { + _ = macho; + _ = pt; + _ = uav_val; + _ = uav_align; + @panic("TODO"); +} +pub fn relocSymAddr( + macho: *MachO, + reloc_info: link.File.RelocInfo, +) link.Error!void { + _ = macho; + _ = reloc_info; + @panic("TODO"); +} + +pub fn loadInput(macho: *MachO, input: link.Input) link.Error!void { + _ = macho; + _ = input; + @panic("TODO"); +} +pub fn prelink(macho: *MachO, prog_node: std.Progress.Node) link.Error!void { + _ = macho; + _ = prog_node; + @panic("TODO"); +} +pub fn flush( + macho: *MachO, + arena: std.mem.Allocator, + tid: Zcu.PerThread.Id, + prog_node: std.Progress.Node, +) link.Error!void { + _ = macho; + _ = arena; + _ = tid; + _ = prog_node; + @panic("TODO"); +} +pub fn updateErrorData(macho: *MachO, pt: Zcu.PerThread) link.Error!void { + _ = macho; + _ = pt; + @panic("TODO"); +} +pub fn idle(macho: *MachO, tid: Zcu.PerThread.Id) link.Error!bool { + _ = macho; + _ = tid; + @panic("TODO"); +} + +pub fn updateFunc( + macho: *MachO, + pt: Zcu.PerThread, + func_index: InternPool.Index, + mir: *const codegen.AnyMir, +) link.Error!void { + _ = macho; + _ = pt; + _ = func_index; + _ = mir; + @panic("TODO"); +} +pub fn updateNav(macho: *MachO, pt: Zcu.PerThread, nav_index: InternPool.Nav.Index) link.Error!void { + _ = macho; + _ = pt; + _ = nav_index; + @panic("TODO"); +} +pub fn updateExports( + macho: *MachO, + pt: Zcu.PerThread, + export_indices: []const Zcu.Export.Index, +) link.Error!void { + _ = macho; + _ = pt; + _ = export_indices; + @panic("TODO"); +} + +pub fn dump(macho: *MachO, w: *Io.Writer, tid: Zcu.PerThread.Id) !link.File.DumpResult { + if (macho.options.enable_link_snapshots) { + try macho.printNode(tid, w, .root, 0); + return .enabled; + } + return .disabled; +} +fn printNode( + macho: *MachO, + tid: Zcu.PerThread.Id, + w: *Io.Writer, + ni: MappedFile.Node.Index, + indent: usize, +) Io.Writer.Error!void { + const node = macho.getNode(ni); + try w.splatByteAll(' ', indent); + switch (node) { + .macho => try w.writeAll("macho"), + } + { + const mf_node = &macho.mf.nodes.items[@backingInt(ni)]; + const off, const size = mf_node.location().resolve(&macho.mf); + try w.print(" index={d} offset=0x{x} size=0x{x} align=0x{x} {t}{s}{s}{s}{s}{s}{s}\n", .{ + @backingInt(ni), + off, + size, + mf_node.flags.alignment.toByteUnits(), + mf_node.flags.position, + if (mf_node.flags.bubbles_moved) " bubbles_moved" else "", + if (mf_node.flags.resized) " moved" else "", + if (mf_node.flags.resized) " resized" else "", + if (mf_node.flags.enable_next_moved) " enable_next_moved" else "", + if (mf_node.flags.next_moved) " next_moved" else "", + if (mf_node.flags.has_content) " has_content" else "", + }); + } + if (ni.first(&macho.mf).unwrap()) |first_ni| { + // non-leaf, just print children + var child_ni = first_ni; + while (true) { + try macho.printNode(tid, w, child_ni, indent + 1); + child_ni = child_ni.next(&macho.mf).unwrap() orelse break; + } + return; + } + const file_loc = ni.fileLocation(&macho.mf, false); + var address = file_loc.offset; + if (file_loc.size == 0) { + try w.splatByteAll(' ', indent + 1); + try w.print("{x:0>8}\n", .{address}); + return; + } + const line_len = 0x10; + var line_it = std.mem.window( + u8, + macho.mf.memory_map.memory[@intCast(file_loc.offset)..][0..@intCast(file_loc.size)], + line_len, + line_len, + ); + while (line_it.next()) |line_bytes| : (address += line_len) { + try w.splatByteAll(' ', indent + 1); + try w.print("{x:0>8} ", .{address}); + for (line_bytes) |byte| try w.print("{x:0>2} ", .{byte}); + try w.splatByteAll(' ', 3 * (line_len - line_bytes.len) + 1); + for (line_bytes) |byte| try w.writeByte(if (std.ascii.isPrint(byte)) byte else '.'); + try w.writeByte('\n'); + } +} + +const std = @import("std"); +const Io = std.Io; + +const link = @import("../link.zig"); +const codegen = @import("../codegen.zig"); +const Compilation = @import("../Compilation.zig"); +const InternPool = @import("../InternPool.zig"); +const MappedFile = @import("MappedFile.zig"); +const Zcu = @import("../Zcu.zig"); diff --git a/src/target.zig b/src/target.zig index 81d39c22fa4d81129016bd58b04c97a6e3e7a1b6..5e36be5fd4f7c7ad799f619e775bcc611747003f 100644 --- a/src/target.zig +++ b/src/target.zig @@ -299,7 +299,7 @@ pub fn preferNewLinkerOverLld(target: *const std.Target) bool { /// Returns `true` if `ofmt` has two linker implementations, so `-fnew-linker` is meaningful. pub fn hasNewLinker(ofmt: std.Target.ObjectFormat) bool { return switch (ofmt) { - .elf => true, + .elf, .macho => true, else => false, }; } -- 2.54.0