Commit 828f61e8df

Jakub Konka <kubkon@jakubkonka.com>
2021-12-10 14:13:43
macho: move all helpers from commands.zig into std.macho
This way we will finally be able to share common parsing logic between different Zig components and 3rd party packages.
1 parent 81e7d85
lib/std/macho.zig
@@ -1,4 +1,12 @@
 const std = @import("std");
+const builtin = @import("builtin");
+const assert = std.debug.assert;
+const io = std.io;
+const mem = std.mem;
+const meta = std.meta;
+const testing = std.testing;
+
+const Allocator = mem.Allocator;
 
 pub const mach_header = extern struct {
     magic: u32,
@@ -770,7 +778,7 @@ pub const section_64 = extern struct {
 };
 
 fn parseName(name: *const [16]u8) []const u8 {
-    const len = std.mem.indexOfScalar(u8, name, @as(u8, 0)) orelse name.len;
+    const len = mem.indexOfScalar(u8, name, @as(u8, 0)) orelse name.len;
     return name[0..len];
 }
 
@@ -1804,3 +1812,428 @@ pub const data_in_code_entry = extern struct {
     /// A DICE_KIND value.
     kind: u16,
 };
+
+/// A Zig wrapper for all known MachO load commands.
+/// Provides interface to read and write the load command data to a buffer.
+pub const LoadCommand = union(enum) {
+    segment: SegmentCommand,
+    dyld_info_only: dyld_info_command,
+    symtab: symtab_command,
+    dysymtab: dysymtab_command,
+    dylinker: GenericCommandWithData(dylinker_command),
+    dylib: GenericCommandWithData(dylib_command),
+    main: entry_point_command,
+    version_min: version_min_command,
+    source_version: source_version_command,
+    build_version: GenericCommandWithData(build_version_command),
+    uuid: uuid_command,
+    linkedit_data: linkedit_data_command,
+    rpath: GenericCommandWithData(rpath_command),
+    unknown: GenericCommandWithData(load_command),
+
+    pub fn read(allocator: Allocator, reader: anytype) !LoadCommand {
+        const header = try reader.readStruct(load_command);
+        var buffer = try allocator.alloc(u8, header.cmdsize);
+        defer allocator.free(buffer);
+        mem.copy(u8, buffer, mem.asBytes(&header));
+        try reader.readNoEof(buffer[@sizeOf(load_command)..]);
+        var stream = io.fixedBufferStream(buffer);
+
+        return switch (header.cmd) {
+            LC_SEGMENT_64 => LoadCommand{
+                .segment = try SegmentCommand.read(allocator, stream.reader()),
+            },
+            LC_DYLD_INFO, LC_DYLD_INFO_ONLY => LoadCommand{
+                .dyld_info_only = try stream.reader().readStruct(dyld_info_command),
+            },
+            LC_SYMTAB => LoadCommand{
+                .symtab = try stream.reader().readStruct(symtab_command),
+            },
+            LC_DYSYMTAB => LoadCommand{
+                .dysymtab = try stream.reader().readStruct(dysymtab_command),
+            },
+            LC_ID_DYLINKER, LC_LOAD_DYLINKER, LC_DYLD_ENVIRONMENT => LoadCommand{
+                .dylinker = try GenericCommandWithData(dylinker_command).read(allocator, stream.reader()),
+            },
+            LC_ID_DYLIB, LC_LOAD_WEAK_DYLIB, LC_LOAD_DYLIB, LC_REEXPORT_DYLIB => LoadCommand{
+                .dylib = try GenericCommandWithData(dylib_command).read(allocator, stream.reader()),
+            },
+            LC_MAIN => LoadCommand{
+                .main = try stream.reader().readStruct(entry_point_command),
+            },
+            LC_VERSION_MIN_MACOSX, LC_VERSION_MIN_IPHONEOS, LC_VERSION_MIN_WATCHOS, LC_VERSION_MIN_TVOS => LoadCommand{
+                .version_min = try stream.reader().readStruct(version_min_command),
+            },
+            LC_SOURCE_VERSION => LoadCommand{
+                .source_version = try stream.reader().readStruct(source_version_command),
+            },
+            LC_BUILD_VERSION => LoadCommand{
+                .build_version = try GenericCommandWithData(build_version_command).read(allocator, stream.reader()),
+            },
+            LC_UUID => LoadCommand{
+                .uuid = try stream.reader().readStruct(uuid_command),
+            },
+            LC_FUNCTION_STARTS, LC_DATA_IN_CODE, LC_CODE_SIGNATURE => LoadCommand{
+                .linkedit_data = try stream.reader().readStruct(linkedit_data_command),
+            },
+            LC_RPATH => LoadCommand{
+                .rpath = try GenericCommandWithData(rpath_command).read(allocator, stream.reader()),
+            },
+            else => LoadCommand{
+                .unknown = try GenericCommandWithData(load_command).read(allocator, stream.reader()),
+            },
+        };
+    }
+
+    pub fn write(self: LoadCommand, writer: anytype) !void {
+        return switch (self) {
+            .dyld_info_only => |x| writeStruct(x, writer),
+            .symtab => |x| writeStruct(x, writer),
+            .dysymtab => |x| writeStruct(x, writer),
+            .main => |x| writeStruct(x, writer),
+            .version_min => |x| writeStruct(x, writer),
+            .source_version => |x| writeStruct(x, writer),
+            .uuid => |x| writeStruct(x, writer),
+            .linkedit_data => |x| writeStruct(x, writer),
+            .segment => |x| x.write(writer),
+            .dylinker => |x| x.write(writer),
+            .dylib => |x| x.write(writer),
+            .rpath => |x| x.write(writer),
+            .build_version => |x| x.write(writer),
+            .unknown => |x| x.write(writer),
+        };
+    }
+
+    pub fn cmd(self: LoadCommand) u32 {
+        return switch (self) {
+            .dyld_info_only => |x| x.cmd,
+            .symtab => |x| x.cmd,
+            .dysymtab => |x| x.cmd,
+            .main => |x| x.cmd,
+            .version_min => |x| x.cmd,
+            .source_version => |x| x.cmd,
+            .uuid => |x| x.cmd,
+            .linkedit_data => |x| x.cmd,
+            .segment => |x| x.inner.cmd,
+            .dylinker => |x| x.inner.cmd,
+            .dylib => |x| x.inner.cmd,
+            .rpath => |x| x.inner.cmd,
+            .build_version => |x| x.inner.cmd,
+            .unknown => |x| x.inner.cmd,
+        };
+    }
+
+    pub fn cmdsize(self: LoadCommand) u32 {
+        return switch (self) {
+            .dyld_info_only => |x| x.cmdsize,
+            .symtab => |x| x.cmdsize,
+            .dysymtab => |x| x.cmdsize,
+            .main => |x| x.cmdsize,
+            .version_min => |x| x.cmdsize,
+            .source_version => |x| x.cmdsize,
+            .linkedit_data => |x| x.cmdsize,
+            .uuid => |x| x.cmdsize,
+            .segment => |x| x.inner.cmdsize,
+            .dylinker => |x| x.inner.cmdsize,
+            .dylib => |x| x.inner.cmdsize,
+            .rpath => |x| x.inner.cmdsize,
+            .build_version => |x| x.inner.cmdsize,
+            .unknown => |x| x.inner.cmdsize,
+        };
+    }
+
+    pub fn deinit(self: *LoadCommand, allocator: Allocator) void {
+        return switch (self.*) {
+            .segment => |*x| x.deinit(allocator),
+            .dylinker => |*x| x.deinit(allocator),
+            .dylib => |*x| x.deinit(allocator),
+            .rpath => |*x| x.deinit(allocator),
+            .build_version => |*x| x.deinit(allocator),
+            .unknown => |*x| x.deinit(allocator),
+            else => {},
+        };
+    }
+
+    fn writeStruct(command: anytype, writer: anytype) !void {
+        return writer.writeAll(mem.asBytes(&command));
+    }
+
+    pub fn eql(self: LoadCommand, other: LoadCommand) bool {
+        if (@as(meta.Tag(LoadCommand), self) != @as(meta.Tag(LoadCommand), other)) return false;
+        return switch (self) {
+            .dyld_info_only => |x| meta.eql(x, other.dyld_info_only),
+            .symtab => |x| meta.eql(x, other.symtab),
+            .dysymtab => |x| meta.eql(x, other.dysymtab),
+            .main => |x| meta.eql(x, other.main),
+            .version_min => |x| meta.eql(x, other.version_min),
+            .source_version => |x| meta.eql(x, other.source_version),
+            .build_version => |x| x.eql(other.build_version),
+            .uuid => |x| meta.eql(x, other.uuid),
+            .linkedit_data => |x| meta.eql(x, other.linkedit_data),
+            .segment => |x| x.eql(other.segment),
+            .dylinker => |x| x.eql(other.dylinker),
+            .dylib => |x| x.eql(other.dylib),
+            .rpath => |x| x.eql(other.rpath),
+            .unknown => |x| x.eql(other.unknown),
+        };
+    }
+};
+
+/// A Zig wrapper for segment_command_64.
+/// Encloses the extern struct together with a list of sections for this segment.
+pub const SegmentCommand = struct {
+    inner: segment_command_64,
+    sections: std.ArrayListUnmanaged(section_64) = .{},
+
+    pub fn read(allocator: Allocator, reader: anytype) !SegmentCommand {
+        const inner = try reader.readStruct(segment_command_64);
+        var segment = SegmentCommand{
+            .inner = inner,
+        };
+        try segment.sections.ensureTotalCapacityPrecise(allocator, inner.nsects);
+
+        var i: usize = 0;
+        while (i < inner.nsects) : (i += 1) {
+            const sect = try reader.readStruct(section_64);
+            segment.sections.appendAssumeCapacity(sect);
+        }
+
+        return segment;
+    }
+
+    pub fn write(self: SegmentCommand, writer: anytype) !void {
+        try writer.writeAll(mem.asBytes(&self.inner));
+        for (self.sections.items) |sect| {
+            try writer.writeAll(mem.asBytes(&sect));
+        }
+    }
+
+    pub fn deinit(self: *SegmentCommand, allocator: Allocator) void {
+        self.sections.deinit(allocator);
+    }
+
+    pub fn eql(self: SegmentCommand, other: SegmentCommand) bool {
+        if (!meta.eql(self.inner, other.inner)) return false;
+        const lhs = self.sections.items;
+        const rhs = other.sections.items;
+        var i: usize = 0;
+        while (i < self.inner.nsects) : (i += 1) {
+            if (!meta.eql(lhs[i], rhs[i])) return false;
+        }
+        return true;
+    }
+};
+
+pub fn emptyGenericCommandWithData(cmd: anytype) GenericCommandWithData(@TypeOf(cmd)) {
+    return .{ .inner = cmd };
+}
+
+/// A Zig wrapper for a generic load command with variable-length data.
+pub fn GenericCommandWithData(comptime Cmd: type) type {
+    return struct {
+        inner: Cmd,
+        /// This field remains undefined until `read` is called.
+        data: []u8 = undefined,
+
+        const Self = @This();
+
+        pub fn read(allocator: Allocator, reader: anytype) !Self {
+            const inner = try reader.readStruct(Cmd);
+            var data = try allocator.alloc(u8, inner.cmdsize - @sizeOf(Cmd));
+            errdefer allocator.free(data);
+            try reader.readNoEof(data);
+            return Self{
+                .inner = inner,
+                .data = data,
+            };
+        }
+
+        pub fn write(self: Self, writer: anytype) !void {
+            try writer.writeAll(mem.asBytes(&self.inner));
+            try writer.writeAll(self.data);
+        }
+
+        pub fn deinit(self: *Self, allocator: Allocator) void {
+            allocator.free(self.data);
+        }
+
+        pub fn eql(self: Self, other: Self) bool {
+            if (!meta.eql(self.inner, other.inner)) return false;
+            return mem.eql(u8, self.data, other.data);
+        }
+    };
+}
+
+pub fn createLoadDylibCommand(
+    allocator: Allocator,
+    name: []const u8,
+    timestamp: u32,
+    current_version: u32,
+    compatibility_version: u32,
+) !GenericCommandWithData(dylib_command) {
+    const cmdsize = @intCast(u32, mem.alignForwardGeneric(
+        u64,
+        @sizeOf(dylib_command) + name.len + 1, // +1 for nul
+        @sizeOf(u64),
+    ));
+
+    var dylib_cmd = emptyGenericCommandWithData(dylib_command{
+        .cmd = LC_LOAD_DYLIB,
+        .cmdsize = cmdsize,
+        .dylib = .{
+            .name = @sizeOf(dylib_command),
+            .timestamp = timestamp,
+            .current_version = current_version,
+            .compatibility_version = compatibility_version,
+        },
+    });
+    dylib_cmd.data = try allocator.alloc(u8, cmdsize - dylib_cmd.inner.dylib.name);
+
+    mem.set(u8, dylib_cmd.data, 0);
+    mem.copy(u8, dylib_cmd.data, name);
+
+    return dylib_cmd;
+}
+
+fn testRead(allocator: Allocator, buffer: []const u8, expected: anytype) !void {
+    var stream = io.fixedBufferStream(buffer);
+    var given = try LoadCommand.read(allocator, stream.reader());
+    defer given.deinit(allocator);
+    try testing.expect(expected.eql(given));
+}
+
+fn testWrite(buffer: []u8, cmd: LoadCommand, expected: []const u8) !void {
+    var stream = io.fixedBufferStream(buffer);
+    try cmd.write(stream.writer());
+    try testing.expect(mem.eql(u8, expected, buffer[0..expected.len]));
+}
+
+fn makeStaticString(bytes: []const u8) [16]u8 {
+    var buf = [_]u8{0} ** 16;
+    assert(bytes.len <= buf.len);
+    mem.copy(u8, &buf, bytes);
+    return buf;
+}
+
+test "read-write segment command" {
+    // TODO compiling for macOS from big-endian arch
+    if (builtin.target.cpu.arch.endian() != .Little) return error.SkipZigTest;
+
+    var gpa = testing.allocator;
+    const in_buffer = &[_]u8{
+        0x19, 0x00, 0x00, 0x00, // cmd
+        0x98, 0x00, 0x00, 0x00, // cmdsize
+        0x5f, 0x5f, 0x54, 0x45, 0x58, 0x54, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // segname
+        0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, // vmaddr
+        0x00, 0x80, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, // vmsize
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // fileoff
+        0x00, 0x80, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, // filesize
+        0x07, 0x00, 0x00, 0x00, // maxprot
+        0x05, 0x00, 0x00, 0x00, // initprot
+        0x01, 0x00, 0x00, 0x00, // nsects
+        0x00, 0x00, 0x00, 0x00, // flags
+        0x5f, 0x5f, 0x74, 0x65, 0x78, 0x74, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // sectname
+        0x5f, 0x5f, 0x54, 0x45, 0x58, 0x54, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // segname
+        0x00, 0x40, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, // address
+        0xc0, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // size
+        0x00, 0x40, 0x00, 0x00, // offset
+        0x02, 0x00, 0x00, 0x00, // alignment
+        0x00, 0x00, 0x00, 0x00, // reloff
+        0x00, 0x00, 0x00, 0x00, // nreloc
+        0x00, 0x04, 0x00, 0x80, // flags
+        0x00, 0x00, 0x00, 0x00, // reserved1
+        0x00, 0x00, 0x00, 0x00, // reserved2
+        0x00, 0x00, 0x00, 0x00, // reserved3
+    };
+    var cmd = SegmentCommand{
+        .inner = .{
+            .cmdsize = 152,
+            .segname = makeStaticString("__TEXT"),
+            .vmaddr = 4294967296,
+            .vmsize = 294912,
+            .filesize = 294912,
+            .maxprot = VM_PROT_READ | VM_PROT_WRITE | VM_PROT_EXECUTE,
+            .initprot = VM_PROT_EXECUTE | VM_PROT_READ,
+            .nsects = 1,
+        },
+    };
+    try cmd.sections.append(gpa, .{
+        .sectname = makeStaticString("__text"),
+        .segname = makeStaticString("__TEXT"),
+        .addr = 4294983680,
+        .size = 448,
+        .offset = 16384,
+        .@"align" = 2,
+        .flags = S_REGULAR | S_ATTR_PURE_INSTRUCTIONS | S_ATTR_SOME_INSTRUCTIONS,
+    });
+    defer cmd.deinit(gpa);
+    try testRead(gpa, in_buffer, LoadCommand{ .segment = cmd });
+
+    var out_buffer: [in_buffer.len]u8 = undefined;
+    try testWrite(&out_buffer, LoadCommand{ .segment = cmd }, in_buffer);
+}
+
+test "read-write generic command with data" {
+    // TODO compiling for macOS from big-endian arch
+    if (builtin.target.cpu.arch.endian() != .Little) return error.SkipZigTest;
+
+    var gpa = testing.allocator;
+    const in_buffer = &[_]u8{
+        0x0c, 0x00, 0x00, 0x00, // cmd
+        0x20, 0x00, 0x00, 0x00, // cmdsize
+        0x18, 0x00, 0x00, 0x00, // name
+        0x02, 0x00, 0x00, 0x00, // timestamp
+        0x00, 0x00, 0x00, 0x00, // current_version
+        0x00, 0x00, 0x00, 0x00, // compatibility_version
+        0x2f, 0x75, 0x73, 0x72, 0x00, 0x00, 0x00, 0x00, // data
+    };
+    var cmd = GenericCommandWithData(dylib_command){
+        .inner = .{
+            .cmd = LC_LOAD_DYLIB,
+            .cmdsize = 32,
+            .dylib = .{
+                .name = 24,
+                .timestamp = 2,
+                .current_version = 0,
+                .compatibility_version = 0,
+            },
+        },
+    };
+    cmd.data = try gpa.alloc(u8, 8);
+    defer gpa.free(cmd.data);
+    cmd.data[0] = 0x2f;
+    cmd.data[1] = 0x75;
+    cmd.data[2] = 0x73;
+    cmd.data[3] = 0x72;
+    cmd.data[4] = 0x0;
+    cmd.data[5] = 0x0;
+    cmd.data[6] = 0x0;
+    cmd.data[7] = 0x0;
+    try testRead(gpa, in_buffer, LoadCommand{ .dylib = cmd });
+
+    var out_buffer: [in_buffer.len]u8 = undefined;
+    try testWrite(&out_buffer, LoadCommand{ .dylib = cmd }, in_buffer);
+}
+
+test "read-write C struct command" {
+    // TODO compiling for macOS from big-endian arch
+    if (builtin.target.cpu.arch.endian() != .Little) return error.SkipZigTest;
+
+    var gpa = testing.allocator;
+    const in_buffer = &[_]u8{
+        0x28, 0x00, 0x00, 0x80, // cmd
+        0x18, 0x00, 0x00, 0x00, // cmdsize
+        0x04, 0x41, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // entryoff
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // stacksize
+    };
+    const cmd = .{
+        .cmd = LC_MAIN,
+        .cmdsize = 24,
+        .entryoff = 16644,
+        .stacksize = 0,
+    };
+    try testRead(gpa, in_buffer, LoadCommand{ .main = cmd });
+
+    var out_buffer: [in_buffer.len]u8 = undefined;
+    try testWrite(&out_buffer, LoadCommand{ .main = cmd }, in_buffer);
+}
src/link/MachO/Atom.zig
@@ -341,7 +341,7 @@ pub fn parseRelocs(self: *Atom, relocs: []macho.relocation_info, context: RelocC
             if (rel.r_extern == 0) {
                 const sect_id = @intCast(u16, rel.r_symbolnum - 1);
                 const local_sym_index = context.object.sections_as_symbols.get(sect_id) orelse blk: {
-                    const seg = context.object.load_commands.items[context.object.segment_cmd_index.?].Segment;
+                    const seg = context.object.load_commands.items[context.object.segment_cmd_index.?].segment;
                     const sect = seg.sections.items[sect_id];
                     const match = (try context.macho_file.getMatchingSection(sect)) orelse
                         unreachable;
@@ -397,7 +397,7 @@ pub fn parseRelocs(self: *Atom, relocs: []macho.relocation_info, context: RelocC
                         else
                             mem.readIntLittle(i32, self.code.items[offset..][0..4]);
                         if (rel.r_extern == 0) {
-                            const seg = context.object.load_commands.items[context.object.segment_cmd_index.?].Segment;
+                            const seg = context.object.load_commands.items[context.object.segment_cmd_index.?].segment;
                             const target_sect_base_addr = seg.sections.items[rel.r_symbolnum - 1].addr;
                             addend -= @intCast(i64, target_sect_base_addr);
                         }
@@ -424,7 +424,7 @@ pub fn parseRelocs(self: *Atom, relocs: []macho.relocation_info, context: RelocC
                         else
                             mem.readIntLittle(i32, self.code.items[offset..][0..4]);
                         if (rel.r_extern == 0) {
-                            const seg = context.object.load_commands.items[context.object.segment_cmd_index.?].Segment;
+                            const seg = context.object.load_commands.items[context.object.segment_cmd_index.?].segment;
                             const target_sect_base_addr = seg.sections.items[rel.r_symbolnum - 1].addr;
                             addend -= @intCast(i64, target_sect_base_addr);
                         }
@@ -446,7 +446,7 @@ pub fn parseRelocs(self: *Atom, relocs: []macho.relocation_info, context: RelocC
                         if (rel.r_extern == 0) {
                             // Note for the future self: when r_extern == 0, we should subtract correction from the
                             // addend.
-                            const seg = context.object.load_commands.items[context.object.segment_cmd_index.?].Segment;
+                            const seg = context.object.load_commands.items[context.object.segment_cmd_index.?].segment;
                             const target_sect_base_addr = seg.sections.items[rel.r_symbolnum - 1].addr;
                             addend += @intCast(i64, context.base_addr + offset + 4) -
                                 @intCast(i64, target_sect_base_addr);
@@ -489,7 +489,7 @@ fn addPtrBindingOrRebase(
         .local => {
             const source_sym = context.macho_file.locals.items[self.local_sym_index];
             const match = context.macho_file.section_ordinals.keys()[source_sym.n_sect - 1];
-            const seg = context.macho_file.load_commands.items[match.seg].Segment;
+            const seg = context.macho_file.load_commands.items[match.seg].segment;
             const sect = seg.sections.items[match.sect];
             const sect_type = sect.type_();
 
@@ -704,7 +704,7 @@ pub fn resolveRelocs(self: *Atom, macho_file: *MachO) !void {
                     const is_tlv = is_tlv: {
                         const source_sym = macho_file.locals.items[self.local_sym_index];
                         const match = macho_file.section_ordinals.keys()[source_sym.n_sect - 1];
-                        const seg = macho_file.load_commands.items[match.seg].Segment;
+                        const seg = macho_file.load_commands.items[match.seg].segment;
                         const sect = seg.sections.items[match.sect];
                         break :is_tlv sect.type_() == macho.S_THREAD_LOCAL_VARIABLES;
                     };
@@ -714,7 +714,7 @@ pub fn resolveRelocs(self: *Atom, macho_file: *MachO) !void {
                         // defined TLV template init section in the following order:
                         // * wrt to __thread_data if defined, then
                         // * wrt to __thread_bss
-                        const seg = macho_file.load_commands.items[macho_file.data_segment_cmd_index.?].Segment;
+                        const seg = macho_file.load_commands.items[macho_file.data_segment_cmd_index.?].segment;
                         const base_address = inner: {
                             if (macho_file.tlv_data_section_index) |i| {
                                 break :inner seg.sections.items[i].addr;
src/link/MachO/commands.zig
@@ -1,463 +0,0 @@
-const std = @import("std");
-const fs = std.fs;
-const io = std.io;
-const mem = std.mem;
-const meta = std.meta;
-const macho = std.macho;
-const testing = std.testing;
-const assert = std.debug.assert;
-
-const Allocator = std.mem.Allocator;
-const MachO = @import("../MachO.zig");
-const makeStaticString = MachO.makeStaticString;
-const padToIdeal = MachO.padToIdeal;
-
-pub const LoadCommand = union(enum) {
-    Segment: SegmentCommand,
-    DyldInfoOnly: macho.dyld_info_command,
-    Symtab: macho.symtab_command,
-    Dysymtab: macho.dysymtab_command,
-    Dylinker: GenericCommandWithData(macho.dylinker_command),
-    Dylib: GenericCommandWithData(macho.dylib_command),
-    Main: macho.entry_point_command,
-    VersionMin: macho.version_min_command,
-    SourceVersion: macho.source_version_command,
-    BuildVersion: GenericCommandWithData(macho.build_version_command),
-    Uuid: macho.uuid_command,
-    LinkeditData: macho.linkedit_data_command,
-    Rpath: GenericCommandWithData(macho.rpath_command),
-    Unknown: GenericCommandWithData(macho.load_command),
-
-    pub fn read(allocator: Allocator, reader: anytype) !LoadCommand {
-        const header = try reader.readStruct(macho.load_command);
-        var buffer = try allocator.alloc(u8, header.cmdsize);
-        defer allocator.free(buffer);
-        mem.copy(u8, buffer, mem.asBytes(&header));
-        try reader.readNoEof(buffer[@sizeOf(macho.load_command)..]);
-        var stream = io.fixedBufferStream(buffer);
-
-        return switch (header.cmd) {
-            macho.LC_SEGMENT_64 => LoadCommand{
-                .Segment = try SegmentCommand.read(allocator, stream.reader()),
-            },
-            macho.LC_DYLD_INFO,
-            macho.LC_DYLD_INFO_ONLY,
-            => LoadCommand{
-                .DyldInfoOnly = try stream.reader().readStruct(macho.dyld_info_command),
-            },
-            macho.LC_SYMTAB => LoadCommand{
-                .Symtab = try stream.reader().readStruct(macho.symtab_command),
-            },
-            macho.LC_DYSYMTAB => LoadCommand{
-                .Dysymtab = try stream.reader().readStruct(macho.dysymtab_command),
-            },
-            macho.LC_ID_DYLINKER,
-            macho.LC_LOAD_DYLINKER,
-            macho.LC_DYLD_ENVIRONMENT,
-            => LoadCommand{
-                .Dylinker = try GenericCommandWithData(macho.dylinker_command).read(allocator, stream.reader()),
-            },
-            macho.LC_ID_DYLIB,
-            macho.LC_LOAD_WEAK_DYLIB,
-            macho.LC_LOAD_DYLIB,
-            macho.LC_REEXPORT_DYLIB,
-            => LoadCommand{
-                .Dylib = try GenericCommandWithData(macho.dylib_command).read(allocator, stream.reader()),
-            },
-            macho.LC_MAIN => LoadCommand{
-                .Main = try stream.reader().readStruct(macho.entry_point_command),
-            },
-            macho.LC_VERSION_MIN_MACOSX,
-            macho.LC_VERSION_MIN_IPHONEOS,
-            macho.LC_VERSION_MIN_WATCHOS,
-            macho.LC_VERSION_MIN_TVOS,
-            => LoadCommand{
-                .VersionMin = try stream.reader().readStruct(macho.version_min_command),
-            },
-            macho.LC_SOURCE_VERSION => LoadCommand{
-                .SourceVersion = try stream.reader().readStruct(macho.source_version_command),
-            },
-            macho.LC_BUILD_VERSION => LoadCommand{
-                .BuildVersion = try GenericCommandWithData(macho.build_version_command).read(allocator, stream.reader()),
-            },
-            macho.LC_UUID => LoadCommand{
-                .Uuid = try stream.reader().readStruct(macho.uuid_command),
-            },
-            macho.LC_FUNCTION_STARTS,
-            macho.LC_DATA_IN_CODE,
-            macho.LC_CODE_SIGNATURE,
-            => LoadCommand{
-                .LinkeditData = try stream.reader().readStruct(macho.linkedit_data_command),
-            },
-            macho.LC_RPATH => LoadCommand{
-                .Rpath = try GenericCommandWithData(macho.rpath_command).read(allocator, stream.reader()),
-            },
-            else => LoadCommand{
-                .Unknown = try GenericCommandWithData(macho.load_command).read(allocator, stream.reader()),
-            },
-        };
-    }
-
-    pub fn write(self: LoadCommand, writer: anytype) !void {
-        return switch (self) {
-            .DyldInfoOnly => |x| writeStruct(x, writer),
-            .Symtab => |x| writeStruct(x, writer),
-            .Dysymtab => |x| writeStruct(x, writer),
-            .Main => |x| writeStruct(x, writer),
-            .VersionMin => |x| writeStruct(x, writer),
-            .SourceVersion => |x| writeStruct(x, writer),
-            .Uuid => |x| writeStruct(x, writer),
-            .LinkeditData => |x| writeStruct(x, writer),
-            .Segment => |x| x.write(writer),
-            .Dylinker => |x| x.write(writer),
-            .Dylib => |x| x.write(writer),
-            .Rpath => |x| x.write(writer),
-            .BuildVersion => |x| x.write(writer),
-            .Unknown => |x| x.write(writer),
-        };
-    }
-
-    pub fn cmd(self: LoadCommand) u32 {
-        return switch (self) {
-            .DyldInfoOnly => |x| x.cmd,
-            .Symtab => |x| x.cmd,
-            .Dysymtab => |x| x.cmd,
-            .Main => |x| x.cmd,
-            .VersionMin => |x| x.cmd,
-            .SourceVersion => |x| x.cmd,
-            .Uuid => |x| x.cmd,
-            .LinkeditData => |x| x.cmd,
-            .Segment => |x| x.inner.cmd,
-            .Dylinker => |x| x.inner.cmd,
-            .Dylib => |x| x.inner.cmd,
-            .Rpath => |x| x.inner.cmd,
-            .BuildVersion => |x| x.inner.cmd,
-            .Unknown => |x| x.inner.cmd,
-        };
-    }
-
-    pub fn cmdsize(self: LoadCommand) u32 {
-        return switch (self) {
-            .DyldInfoOnly => |x| x.cmdsize,
-            .Symtab => |x| x.cmdsize,
-            .Dysymtab => |x| x.cmdsize,
-            .Main => |x| x.cmdsize,
-            .VersionMin => |x| x.cmdsize,
-            .SourceVersion => |x| x.cmdsize,
-            .LinkeditData => |x| x.cmdsize,
-            .Uuid => |x| x.cmdsize,
-            .Segment => |x| x.inner.cmdsize,
-            .Dylinker => |x| x.inner.cmdsize,
-            .Dylib => |x| x.inner.cmdsize,
-            .Rpath => |x| x.inner.cmdsize,
-            .BuildVersion => |x| x.inner.cmdsize,
-            .Unknown => |x| x.inner.cmdsize,
-        };
-    }
-
-    pub fn deinit(self: *LoadCommand, allocator: Allocator) void {
-        return switch (self.*) {
-            .Segment => |*x| x.deinit(allocator),
-            .Dylinker => |*x| x.deinit(allocator),
-            .Dylib => |*x| x.deinit(allocator),
-            .Rpath => |*x| x.deinit(allocator),
-            .BuildVersion => |*x| x.deinit(allocator),
-            .Unknown => |*x| x.deinit(allocator),
-            else => {},
-        };
-    }
-
-    fn writeStruct(command: anytype, writer: anytype) !void {
-        return writer.writeAll(mem.asBytes(&command));
-    }
-
-    fn eql(self: LoadCommand, other: LoadCommand) bool {
-        if (@as(meta.Tag(LoadCommand), self) != @as(meta.Tag(LoadCommand), other)) return false;
-        return switch (self) {
-            .DyldInfoOnly => |x| meta.eql(x, other.DyldInfoOnly),
-            .Symtab => |x| meta.eql(x, other.Symtab),
-            .Dysymtab => |x| meta.eql(x, other.Dysymtab),
-            .Main => |x| meta.eql(x, other.Main),
-            .VersionMin => |x| meta.eql(x, other.VersionMin),
-            .SourceVersion => |x| meta.eql(x, other.SourceVersion),
-            .BuildVersion => |x| x.eql(other.BuildVersion),
-            .Uuid => |x| meta.eql(x, other.Uuid),
-            .LinkeditData => |x| meta.eql(x, other.LinkeditData),
-            .Segment => |x| x.eql(other.Segment),
-            .Dylinker => |x| x.eql(other.Dylinker),
-            .Dylib => |x| x.eql(other.Dylib),
-            .Rpath => |x| x.eql(other.Rpath),
-            .Unknown => |x| x.eql(other.Unknown),
-        };
-    }
-};
-
-pub const SegmentCommand = struct {
-    inner: macho.segment_command_64,
-    sections: std.ArrayListUnmanaged(macho.section_64) = .{},
-
-    pub fn read(alloc: Allocator, reader: anytype) !SegmentCommand {
-        const inner = try reader.readStruct(macho.segment_command_64);
-        var segment = SegmentCommand{
-            .inner = inner,
-        };
-        try segment.sections.ensureTotalCapacityPrecise(alloc, inner.nsects);
-
-        var i: usize = 0;
-        while (i < inner.nsects) : (i += 1) {
-            const section = try reader.readStruct(macho.section_64);
-            segment.sections.appendAssumeCapacity(section);
-        }
-
-        return segment;
-    }
-
-    pub fn write(self: SegmentCommand, writer: anytype) !void {
-        try writer.writeAll(mem.asBytes(&self.inner));
-        for (self.sections.items) |sect| {
-            try writer.writeAll(mem.asBytes(&sect));
-        }
-    }
-
-    pub fn deinit(self: *SegmentCommand, alloc: Allocator) void {
-        self.sections.deinit(alloc);
-    }
-
-    pub fn allocatedSize(self: SegmentCommand, start: u64) u64 {
-        assert(start >= self.inner.fileoff);
-        var min_pos: u64 = self.inner.fileoff + self.inner.filesize;
-        for (self.sections.items) |section| {
-            if (section.offset <= start) continue;
-            if (section.offset < min_pos) min_pos = section.offset;
-        }
-        return min_pos - start;
-    }
-
-    fn detectAllocCollision(self: SegmentCommand, start: u64, size: u64) ?u64 {
-        const end = start + padToIdeal(size);
-        for (self.sections.items) |section| {
-            const increased_size = padToIdeal(section.size);
-            const test_end = section.offset + increased_size;
-            if (end > section.offset and start < test_end) {
-                return test_end;
-            }
-        }
-        return null;
-    }
-
-    pub fn findFreeSpace(self: SegmentCommand, object_size: u64, min_alignment: u64, start: ?u64) u64 {
-        var offset: u64 = if (start) |v| v else self.inner.fileoff;
-        while (self.detectAllocCollision(offset, object_size)) |item_end| {
-            offset = mem.alignForwardGeneric(u64, item_end, min_alignment);
-        }
-        return offset;
-    }
-
-    fn eql(self: SegmentCommand, other: SegmentCommand) bool {
-        if (!meta.eql(self.inner, other.inner)) return false;
-        const lhs = self.sections.items;
-        const rhs = other.sections.items;
-        var i: usize = 0;
-        while (i < self.inner.nsects) : (i += 1) {
-            if (!meta.eql(lhs[i], rhs[i])) return false;
-        }
-        return true;
-    }
-};
-
-pub fn emptyGenericCommandWithData(cmd: anytype) GenericCommandWithData(@TypeOf(cmd)) {
-    return .{ .inner = cmd };
-}
-
-pub fn GenericCommandWithData(comptime Cmd: type) type {
-    return struct {
-        inner: Cmd,
-        /// This field remains undefined until `read` is called.
-        data: []u8 = undefined,
-
-        const Self = @This();
-
-        pub fn read(allocator: Allocator, reader: anytype) !Self {
-            const inner = try reader.readStruct(Cmd);
-            var data = try allocator.alloc(u8, inner.cmdsize - @sizeOf(Cmd));
-            errdefer allocator.free(data);
-            try reader.readNoEof(data);
-            return Self{
-                .inner = inner,
-                .data = data,
-            };
-        }
-
-        pub fn write(self: Self, writer: anytype) !void {
-            try writer.writeAll(mem.asBytes(&self.inner));
-            try writer.writeAll(self.data);
-        }
-
-        pub fn deinit(self: *Self, allocator: Allocator) void {
-            allocator.free(self.data);
-        }
-
-        fn eql(self: Self, other: Self) bool {
-            if (!meta.eql(self.inner, other.inner)) return false;
-            return mem.eql(u8, self.data, other.data);
-        }
-    };
-}
-
-pub fn createLoadDylibCommand(
-    allocator: Allocator,
-    name: []const u8,
-    timestamp: u32,
-    current_version: u32,
-    compatibility_version: u32,
-) !GenericCommandWithData(macho.dylib_command) {
-    const cmdsize = @intCast(u32, mem.alignForwardGeneric(
-        u64,
-        @sizeOf(macho.dylib_command) + name.len + 1, // +1 for nul
-        @sizeOf(u64),
-    ));
-
-    var dylib_cmd = emptyGenericCommandWithData(macho.dylib_command{
-        .cmd = macho.LC_LOAD_DYLIB,
-        .cmdsize = cmdsize,
-        .dylib = .{
-            .name = @sizeOf(macho.dylib_command),
-            .timestamp = timestamp,
-            .current_version = current_version,
-            .compatibility_version = compatibility_version,
-        },
-    });
-    dylib_cmd.data = try allocator.alloc(u8, cmdsize - dylib_cmd.inner.dylib.name);
-
-    mem.set(u8, dylib_cmd.data, 0);
-    mem.copy(u8, dylib_cmd.data, name);
-
-    return dylib_cmd;
-}
-
-fn testRead(allocator: Allocator, buffer: []const u8, expected: anytype) !void {
-    var stream = io.fixedBufferStream(buffer);
-    var given = try LoadCommand.read(allocator, stream.reader());
-    defer given.deinit(allocator);
-    try testing.expect(expected.eql(given));
-}
-
-fn testWrite(buffer: []u8, cmd: LoadCommand, expected: []const u8) !void {
-    var stream = io.fixedBufferStream(buffer);
-    try cmd.write(stream.writer());
-    try testing.expect(mem.eql(u8, expected, buffer[0..expected.len]));
-}
-
-test "read-write segment command" {
-    var gpa = testing.allocator;
-    const in_buffer = &[_]u8{
-        0x19, 0x00, 0x00, 0x00, // cmd
-        0x98, 0x00, 0x00, 0x00, // cmdsize
-        0x5f, 0x5f, 0x54, 0x45, 0x58, 0x54, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // segname
-        0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, // vmaddr
-        0x00, 0x80, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, // vmsize
-        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // fileoff
-        0x00, 0x80, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, // filesize
-        0x07, 0x00, 0x00, 0x00, // maxprot
-        0x05, 0x00, 0x00, 0x00, // initprot
-        0x01, 0x00, 0x00, 0x00, // nsects
-        0x00, 0x00, 0x00, 0x00, // flags
-        0x5f, 0x5f, 0x74, 0x65, 0x78, 0x74, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // sectname
-        0x5f, 0x5f, 0x54, 0x45, 0x58, 0x54, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // segname
-        0x00, 0x40, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, // address
-        0xc0, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // size
-        0x00, 0x40, 0x00, 0x00, // offset
-        0x02, 0x00, 0x00, 0x00, // alignment
-        0x00, 0x00, 0x00, 0x00, // reloff
-        0x00, 0x00, 0x00, 0x00, // nreloc
-        0x00, 0x04, 0x00, 0x80, // flags
-        0x00, 0x00, 0x00, 0x00, // reserved1
-        0x00, 0x00, 0x00, 0x00, // reserved2
-        0x00, 0x00, 0x00, 0x00, // reserved3
-    };
-    var cmd = SegmentCommand{
-        .inner = .{
-            .cmdsize = 152,
-            .segname = makeStaticString("__TEXT"),
-            .vmaddr = 4294967296,
-            .vmsize = 294912,
-            .filesize = 294912,
-            .maxprot = macho.VM_PROT_READ | macho.VM_PROT_WRITE | macho.VM_PROT_EXECUTE,
-            .initprot = macho.VM_PROT_EXECUTE | macho.VM_PROT_READ,
-            .nsects = 1,
-        },
-    };
-    try cmd.sections.append(gpa, .{
-        .sectname = makeStaticString("__text"),
-        .segname = makeStaticString("__TEXT"),
-        .addr = 4294983680,
-        .size = 448,
-        .offset = 16384,
-        .@"align" = 2,
-        .flags = macho.S_REGULAR | macho.S_ATTR_PURE_INSTRUCTIONS | macho.S_ATTR_SOME_INSTRUCTIONS,
-    });
-    defer cmd.deinit(gpa);
-    try testRead(gpa, in_buffer, LoadCommand{ .Segment = cmd });
-
-    var out_buffer: [in_buffer.len]u8 = undefined;
-    try testWrite(&out_buffer, LoadCommand{ .Segment = cmd }, in_buffer);
-}
-
-test "read-write generic command with data" {
-    var gpa = testing.allocator;
-    const in_buffer = &[_]u8{
-        0x0c, 0x00, 0x00, 0x00, // cmd
-        0x20, 0x00, 0x00, 0x00, // cmdsize
-        0x18, 0x00, 0x00, 0x00, // name
-        0x02, 0x00, 0x00, 0x00, // timestamp
-        0x00, 0x00, 0x00, 0x00, // current_version
-        0x00, 0x00, 0x00, 0x00, // compatibility_version
-        0x2f, 0x75, 0x73, 0x72, 0x00, 0x00, 0x00, 0x00, // data
-    };
-    var cmd = GenericCommandWithData(macho.dylib_command){
-        .inner = .{
-            .cmd = macho.LC_LOAD_DYLIB,
-            .cmdsize = 32,
-            .dylib = .{
-                .name = 24,
-                .timestamp = 2,
-                .current_version = 0,
-                .compatibility_version = 0,
-            },
-        },
-    };
-    cmd.data = try gpa.alloc(u8, 8);
-    defer gpa.free(cmd.data);
-    cmd.data[0] = 0x2f;
-    cmd.data[1] = 0x75;
-    cmd.data[2] = 0x73;
-    cmd.data[3] = 0x72;
-    cmd.data[4] = 0x0;
-    cmd.data[5] = 0x0;
-    cmd.data[6] = 0x0;
-    cmd.data[7] = 0x0;
-    try testRead(gpa, in_buffer, LoadCommand{ .Dylib = cmd });
-
-    var out_buffer: [in_buffer.len]u8 = undefined;
-    try testWrite(&out_buffer, LoadCommand{ .Dylib = cmd }, in_buffer);
-}
-
-test "read-write C struct command" {
-    var gpa = testing.allocator;
-    const in_buffer = &[_]u8{
-        0x28, 0x00, 0x00, 0x80, // cmd
-        0x18, 0x00, 0x00, 0x00, // cmdsize
-        0x04, 0x41, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // entryoff
-        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // stacksize
-    };
-    const cmd = .{
-        .cmd = macho.LC_MAIN,
-        .cmdsize = 24,
-        .entryoff = 16644,
-        .stacksize = 0,
-    };
-    try testRead(gpa, in_buffer, LoadCommand{ .Main = cmd });
-
-    var out_buffer: [in_buffer.len]u8 = undefined;
-    try testWrite(&out_buffer, LoadCommand{ .Main = cmd }, in_buffer);
-}
src/link/MachO/DebugSymbols.zig
@@ -12,15 +12,12 @@ const leb = std.leb;
 const Allocator = mem.Allocator;
 
 const build_options = @import("build_options");
-const commands = @import("commands.zig");
 const trace = @import("../../tracy.zig").trace;
-const LoadCommand = commands.LoadCommand;
 const Module = @import("../../Module.zig");
 const Type = @import("../../type.zig").Type;
 const link = @import("../../link.zig");
 const MachO = @import("../MachO.zig");
 const TextBlock = MachO.TextBlock;
-const SegmentCommand = commands.SegmentCommand;
 const SrcFn = MachO.SrcFn;
 const makeStaticString = MachO.makeStaticString;
 const padToIdeal = MachO.padToIdeal;
@@ -31,7 +28,7 @@ base: *MachO,
 file: fs.File,
 
 /// Table of all load commands
-load_commands: std.ArrayListUnmanaged(LoadCommand) = .{},
+load_commands: std.ArrayListUnmanaged(macho.LoadCommand) = .{},
 /// __PAGEZERO segment
 pagezero_segment_cmd_index: ?u16 = null,
 /// __TEXT segment
@@ -113,7 +110,7 @@ pub fn populateMissingMetadata(self: *DebugSymbols, allocator: Allocator) !void
     }
     if (self.symtab_cmd_index == null) {
         self.symtab_cmd_index = @intCast(u16, self.load_commands.items.len);
-        const base_cmd = self.base.load_commands.items[self.base.symtab_cmd_index.?].Symtab;
+        const base_cmd = self.base.load_commands.items[self.base.symtab_cmd_index.?].symtab;
         const symtab_size = base_cmd.nsyms * @sizeOf(macho.nlist_64);
         const symtab_off = self.findFreeSpaceLinkedit(symtab_size, @sizeOf(macho.nlist_64));
 
@@ -124,7 +121,7 @@ pub fn populateMissingMetadata(self: *DebugSymbols, allocator: Allocator) !void
         log.debug("found string table free space 0x{x} to 0x{x}", .{ strtab_off, strtab_off + base_cmd.strsize });
 
         try self.load_commands.append(allocator, .{
-            .Symtab = .{
+            .symtab = .{
                 .cmd = macho.LC_SYMTAB,
                 .cmdsize = @sizeOf(macho.symtab_command),
                 .symoff = @intCast(u32, symtab_off),
@@ -138,48 +135,48 @@ pub fn populateMissingMetadata(self: *DebugSymbols, allocator: Allocator) !void
     }
     if (self.pagezero_segment_cmd_index == null) {
         self.pagezero_segment_cmd_index = @intCast(u16, self.load_commands.items.len);
-        const base_cmd = self.base.load_commands.items[self.base.pagezero_segment_cmd_index.?].Segment;
+        const base_cmd = self.base.load_commands.items[self.base.pagezero_segment_cmd_index.?].segment;
         const cmd = try self.copySegmentCommand(allocator, base_cmd);
-        try self.load_commands.append(allocator, .{ .Segment = cmd });
+        try self.load_commands.append(allocator, .{ .segment = cmd });
         self.load_commands_dirty = true;
     }
     if (self.text_segment_cmd_index == null) {
         self.text_segment_cmd_index = @intCast(u16, self.load_commands.items.len);
-        const base_cmd = self.base.load_commands.items[self.base.text_segment_cmd_index.?].Segment;
+        const base_cmd = self.base.load_commands.items[self.base.text_segment_cmd_index.?].segment;
         const cmd = try self.copySegmentCommand(allocator, base_cmd);
-        try self.load_commands.append(allocator, .{ .Segment = cmd });
+        try self.load_commands.append(allocator, .{ .segment = cmd });
         self.load_commands_dirty = true;
     }
     if (self.data_const_segment_cmd_index == null) outer: {
         if (self.base.data_const_segment_cmd_index == null) break :outer; // __DATA_CONST is optional
         self.data_const_segment_cmd_index = @intCast(u16, self.load_commands.items.len);
-        const base_cmd = self.base.load_commands.items[self.base.data_const_segment_cmd_index.?].Segment;
+        const base_cmd = self.base.load_commands.items[self.base.data_const_segment_cmd_index.?].segment;
         const cmd = try self.copySegmentCommand(allocator, base_cmd);
-        try self.load_commands.append(allocator, .{ .Segment = cmd });
+        try self.load_commands.append(allocator, .{ .segment = cmd });
         self.load_commands_dirty = true;
     }
     if (self.data_segment_cmd_index == null) outer: {
         if (self.base.data_segment_cmd_index == null) break :outer; // __DATA is optional
         self.data_segment_cmd_index = @intCast(u16, self.load_commands.items.len);
-        const base_cmd = self.base.load_commands.items[self.base.data_segment_cmd_index.?].Segment;
+        const base_cmd = self.base.load_commands.items[self.base.data_segment_cmd_index.?].segment;
         const cmd = try self.copySegmentCommand(allocator, base_cmd);
-        try self.load_commands.append(allocator, .{ .Segment = cmd });
+        try self.load_commands.append(allocator, .{ .segment = cmd });
         self.load_commands_dirty = true;
     }
     if (self.linkedit_segment_cmd_index == null) {
         self.linkedit_segment_cmd_index = @intCast(u16, self.load_commands.items.len);
-        const base_cmd = self.base.load_commands.items[self.base.linkedit_segment_cmd_index.?].Segment;
+        const base_cmd = self.base.load_commands.items[self.base.linkedit_segment_cmd_index.?].segment;
         var cmd = try self.copySegmentCommand(allocator, base_cmd);
         cmd.inner.vmsize = self.linkedit_size;
         cmd.inner.fileoff = self.linkedit_off;
         cmd.inner.filesize = self.linkedit_size;
-        try self.load_commands.append(allocator, .{ .Segment = cmd });
+        try self.load_commands.append(allocator, .{ .segment = cmd });
         self.load_commands_dirty = true;
     }
     if (self.dwarf_segment_cmd_index == null) {
         self.dwarf_segment_cmd_index = @intCast(u16, self.load_commands.items.len);
 
-        const linkedit = self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment;
+        const linkedit = self.load_commands.items[self.linkedit_segment_cmd_index.?].segment;
         const ideal_size: u16 = 200 + 128 + 160 + 250;
         const needed_size = mem.alignForwardGeneric(u64, padToIdeal(ideal_size), page_size);
         const off = linkedit.inner.fileoff + linkedit.inner.filesize;
@@ -188,7 +185,7 @@ pub fn populateMissingMetadata(self: *DebugSymbols, allocator: Allocator) !void
         log.debug("found __DWARF segment free space 0x{x} to 0x{x}", .{ off, off + needed_size });
 
         try self.load_commands.append(allocator, .{
-            .Segment = .{
+            .segment = .{
                 .inner = .{
                     .segname = makeStaticString("__DWARF"),
                     .vmaddr = vmaddr,
@@ -228,7 +225,7 @@ pub fn populateMissingMetadata(self: *DebugSymbols, allocator: Allocator) !void
 }
 
 fn allocateSection(self: *DebugSymbols, sectname: []const u8, size: u64, alignment: u16) !u16 {
-    const seg = &self.load_commands.items[self.dwarf_segment_cmd_index.?].Segment;
+    const seg = &self.load_commands.items[self.dwarf_segment_cmd_index.?].segment;
     var sect = macho.section_64{
         .sectname = makeStaticString(sectname),
         .segname = seg.inner.segname,
@@ -236,7 +233,7 @@ fn allocateSection(self: *DebugSymbols, sectname: []const u8, size: u64, alignme
         .@"align" = alignment,
     };
     const alignment_pow_2 = try math.powi(u32, 2, alignment);
-    const off = seg.findFreeSpace(size, alignment_pow_2, null);
+    const off = self.findFreeSpace(size, alignment_pow_2);
 
     assert(off + size <= seg.inner.fileoff + seg.inner.filesize); // TODO expand
 
@@ -268,6 +265,28 @@ fn allocateSection(self: *DebugSymbols, sectname: []const u8, size: u64, alignme
     return index;
 }
 
+fn detectAllocCollision(self: *DebugSymbols, start: u64, size: u64) ?u64 {
+    const seg = self.load_commands.items[self.dwarf_segment_cmd_index.?].segment;
+    const end = start + padToIdeal(size);
+    for (seg.sections.items) |section| {
+        const increased_size = padToIdeal(section.size);
+        const test_end = section.offset + increased_size;
+        if (end > section.offset and start < test_end) {
+            return test_end;
+        }
+    }
+    return null;
+}
+
+fn findFreeSpace(self: *DebugSymbols, object_size: u64, min_alignment: u64) u64 {
+    const seg = self.load_commands.items[self.dwarf_segment_cmd_index.?].segment;
+    var offset: u64 = seg.inner.fileoff;
+    while (self.detectAllocCollision(offset, object_size)) |item_end| {
+        offset = mem.alignForwardGeneric(u64, item_end, min_alignment);
+    }
+    return offset;
+}
+
 pub fn flushModule(self: *DebugSymbols, allocator: Allocator, options: link.Options) !void {
     // TODO This linker code currently assumes there is only 1 compilation unit and it corresponds to the
     // Zig source code.
@@ -275,7 +294,7 @@ pub fn flushModule(self: *DebugSymbols, allocator: Allocator, options: link.Opti
     const init_len_size: usize = 4;
 
     if (self.debug_abbrev_section_dirty) {
-        const dwarf_segment = &self.load_commands.items[self.dwarf_segment_cmd_index.?].Segment;
+        const dwarf_segment = &self.load_commands.items[self.dwarf_segment_cmd_index.?].segment;
         const debug_abbrev_sect = &dwarf_segment.sections.items[self.debug_abbrev_section_index.?];
 
         // These are LEB encoded but since the values are all less than 127
@@ -320,10 +339,10 @@ pub fn flushModule(self: *DebugSymbols, allocator: Allocator, options: link.Opti
         };
 
         const needed_size = abbrev_buf.len;
-        const allocated_size = dwarf_segment.allocatedSize(debug_abbrev_sect.offset);
+        const allocated_size = self.allocatedSize(debug_abbrev_sect.offset);
         if (needed_size > allocated_size) {
             debug_abbrev_sect.size = 0; // free the space
-            const offset = dwarf_segment.findFreeSpace(needed_size, 1, null);
+            const offset = self.findFreeSpace(needed_size, 1);
             debug_abbrev_sect.offset = @intCast(u32, offset);
             debug_abbrev_sect.addr = dwarf_segment.inner.vmaddr + offset - dwarf_segment.inner.fileoff;
         }
@@ -345,7 +364,7 @@ pub fn flushModule(self: *DebugSymbols, allocator: Allocator, options: link.Opti
         // leave debug_info_header_dirty=true.
         const first_dbg_info_decl = self.dbg_info_decl_first orelse break :debug_info;
         const last_dbg_info_decl = self.dbg_info_decl_last.?;
-        const dwarf_segment = &self.load_commands.items[self.dwarf_segment_cmd_index.?].Segment;
+        const dwarf_segment = &self.load_commands.items[self.dwarf_segment_cmd_index.?].segment;
         const debug_info_sect = &dwarf_segment.sections.items[self.debug_info_section_index.?];
 
         // We have a function to compute the upper bound size, because it's needed
@@ -372,7 +391,7 @@ pub fn flushModule(self: *DebugSymbols, allocator: Allocator, options: link.Opti
         const producer_strp = try self.makeDebugString(allocator, link.producer_string);
         // Currently only one compilation unit is supported, so the address range is simply
         // identical to the main program header virtual address and memory size.
-        const text_segment = self.load_commands.items[self.text_segment_cmd_index.?].Segment;
+        const text_segment = self.load_commands.items[self.text_segment_cmd_index.?].segment;
         const text_section = text_segment.sections.items[self.text_section_index.?];
         const low_pc = text_section.addr;
         const high_pc = text_section.addr + text_section.size;
@@ -399,7 +418,7 @@ pub fn flushModule(self: *DebugSymbols, allocator: Allocator, options: link.Opti
     }
 
     if (self.debug_aranges_section_dirty) {
-        const dwarf_segment = &self.load_commands.items[self.dwarf_segment_cmd_index.?].Segment;
+        const dwarf_segment = &self.load_commands.items[self.dwarf_segment_cmd_index.?].segment;
         const debug_aranges_sect = &dwarf_segment.sections.items[self.debug_aranges_section_index.?];
 
         // Enough for all the data without resizing. When support for more compilation units
@@ -426,7 +445,7 @@ pub fn flushModule(self: *DebugSymbols, allocator: Allocator, options: link.Opti
 
         // Currently only one compilation unit is supported, so the address range is simply
         // identical to the main program header virtual address and memory size.
-        const text_segment = self.load_commands.items[self.text_segment_cmd_index.?].Segment;
+        const text_segment = self.load_commands.items[self.text_segment_cmd_index.?].segment;
         const text_section = text_segment.sections.items[self.text_section_index.?];
         mem.writeIntLittle(u64, di_buf.addManyAsArrayAssumeCapacity(8), text_section.addr);
         mem.writeIntLittle(u64, di_buf.addManyAsArrayAssumeCapacity(8), text_section.size);
@@ -442,10 +461,10 @@ pub fn flushModule(self: *DebugSymbols, allocator: Allocator, options: link.Opti
         mem.writeIntLittle(u32, di_buf.items[init_len_index..][0..4], @intCast(u32, init_len));
 
         const needed_size = di_buf.items.len;
-        const allocated_size = dwarf_segment.allocatedSize(debug_aranges_sect.offset);
+        const allocated_size = self.allocatedSize(debug_aranges_sect.offset);
         if (needed_size > allocated_size) {
             debug_aranges_sect.size = 0; // free the space
-            const new_offset = dwarf_segment.findFreeSpace(needed_size, 16, null);
+            const new_offset = self.findFreeSpace(needed_size, 16);
             debug_aranges_sect.addr = dwarf_segment.inner.vmaddr + new_offset - dwarf_segment.inner.fileoff;
             debug_aranges_sect.offset = @intCast(u32, new_offset);
         }
@@ -467,7 +486,7 @@ pub fn flushModule(self: *DebugSymbols, allocator: Allocator, options: link.Opti
         const dbg_line_prg_end = self.getDebugLineProgramEnd();
         assert(dbg_line_prg_end != 0);
 
-        const dwarf_segment = &self.load_commands.items[self.dwarf_segment_cmd_index.?].Segment;
+        const dwarf_segment = &self.load_commands.items[self.dwarf_segment_cmd_index.?].segment;
         const debug_line_sect = &dwarf_segment.sections.items[self.debug_line_section_index.?];
 
         // The size of this header is variable, depending on the number of directories,
@@ -540,15 +559,15 @@ pub fn flushModule(self: *DebugSymbols, allocator: Allocator, options: link.Opti
         self.debug_line_header_dirty = false;
     }
     {
-        const dwarf_segment = &self.load_commands.items[self.dwarf_segment_cmd_index.?].Segment;
+        const dwarf_segment = &self.load_commands.items[self.dwarf_segment_cmd_index.?].segment;
         const debug_strtab_sect = &dwarf_segment.sections.items[self.debug_str_section_index.?];
         if (self.debug_string_table_dirty or self.debug_string_table.items.len != debug_strtab_sect.size) {
-            const allocated_size = dwarf_segment.allocatedSize(debug_strtab_sect.offset);
+            const allocated_size = self.allocatedSize(debug_strtab_sect.offset);
             const needed_size = self.debug_string_table.items.len;
 
             if (needed_size > allocated_size) {
                 debug_strtab_sect.size = 0; // free the space
-                const new_offset = dwarf_segment.findFreeSpace(needed_size, 1, null);
+                const new_offset = self.findFreeSpace(needed_size, 1);
                 debug_strtab_sect.addr = dwarf_segment.inner.vmaddr + new_offset - dwarf_segment.inner.fileoff;
                 debug_strtab_sect.offset = @intCast(u32, new_offset);
             }
@@ -588,8 +607,12 @@ pub fn deinit(self: *DebugSymbols, allocator: Allocator) void {
     self.file.close();
 }
 
-fn copySegmentCommand(self: *DebugSymbols, allocator: Allocator, base_cmd: SegmentCommand) !SegmentCommand {
-    var cmd = SegmentCommand{
+fn copySegmentCommand(
+    self: *DebugSymbols,
+    allocator: Allocator,
+    base_cmd: macho.SegmentCommand,
+) !macho.SegmentCommand {
+    var cmd = macho.SegmentCommand{
         .inner = .{
             .segname = undefined,
             .cmdsize = base_cmd.inner.cmdsize,
@@ -633,7 +656,7 @@ fn copySegmentCommand(self: *DebugSymbols, allocator: Allocator, base_cmd: Segme
 }
 
 fn updateDwarfSegment(self: *DebugSymbols) void {
-    const dwarf_segment = &self.load_commands.items[self.dwarf_segment_cmd_index.?].Segment;
+    const dwarf_segment = &self.load_commands.items[self.dwarf_segment_cmd_index.?].segment;
     var file_size: u64 = 0;
     for (dwarf_segment.sections.items) |sect| {
         file_size += sect.size;
@@ -702,7 +725,7 @@ fn allocatedSizeLinkedit(self: *DebugSymbols, start: u64) u64 {
     var min_pos: u64 = std.math.maxInt(u64);
 
     if (self.symtab_cmd_index) |idx| {
-        const symtab = self.load_commands.items[idx].Symtab;
+        const symtab = self.load_commands.items[idx].symtab;
         if (symtab.symoff >= start and symtab.symoff < min_pos) min_pos = symtab.symoff;
         if (symtab.stroff >= start and symtab.stroff < min_pos) min_pos = symtab.stroff;
     }
@@ -710,12 +733,23 @@ fn allocatedSizeLinkedit(self: *DebugSymbols, start: u64) u64 {
     return min_pos - start;
 }
 
+fn allocatedSize(self: *DebugSymbols, start: u64) u64 {
+    const seg = self.load_commands.items[self.dwarf_segment_cmd_index.?].segment;
+    assert(start >= seg.inner.fileoff);
+    var min_pos: u64 = seg.inner.fileoff + seg.inner.filesize;
+    for (seg.sections.items) |section| {
+        if (section.offset <= start) continue;
+        if (section.offset < min_pos) min_pos = section.offset;
+    }
+    return min_pos - start;
+}
+
 fn detectAllocCollisionLinkedit(self: *DebugSymbols, start: u64, size: u64) ?u64 {
     const end = start + padToIdeal(size);
 
     if (self.symtab_cmd_index) |idx| outer: {
         if (self.load_commands.items.len == idx) break :outer;
-        const symtab = self.load_commands.items[idx].Symtab;
+        const symtab = self.load_commands.items[idx].symtab;
         {
             // Symbol table
             const symsize = symtab.nsyms * @sizeOf(macho.nlist_64);
@@ -747,7 +781,7 @@ fn findFreeSpaceLinkedit(self: *DebugSymbols, object_size: u64, min_alignment: u
 }
 
 fn relocateSymbolTable(self: *DebugSymbols) !void {
-    const symtab = &self.load_commands.items[self.symtab_cmd_index.?].Symtab;
+    const symtab = &self.load_commands.items[self.symtab_cmd_index.?].symtab;
     const nlocals = self.base.locals.items.len;
     const nglobals = self.base.globals.items.len;
     const nsyms = nlocals + nglobals;
@@ -780,7 +814,7 @@ pub fn writeLocalSymbol(self: *DebugSymbols, index: usize) !void {
     const tracy = trace(@src());
     defer tracy.end();
     try self.relocateSymbolTable();
-    const symtab = &self.load_commands.items[self.symtab_cmd_index.?].Symtab;
+    const symtab = &self.load_commands.items[self.symtab_cmd_index.?].symtab;
     const off = symtab.symoff + @sizeOf(macho.nlist_64) * index;
     log.debug("writing local symbol {} at 0x{x}", .{ index, off });
     try self.file.pwriteAll(mem.asBytes(&self.base.locals.items[index]), off);
@@ -792,7 +826,7 @@ fn writeStringTable(self: *DebugSymbols) !void {
     const tracy = trace(@src());
     defer tracy.end();
 
-    const symtab = &self.load_commands.items[self.symtab_cmd_index.?].Symtab;
+    const symtab = &self.load_commands.items[self.symtab_cmd_index.?].symtab;
     const allocated_size = self.allocatedSizeLinkedit(symtab.stroff);
     const needed_size = mem.alignForwardGeneric(u64, self.base.strtab.items.len, @alignOf(u64));
 
@@ -816,7 +850,7 @@ pub fn updateDeclLineNumber(self: *DebugSymbols, module: *Module, decl: *const M
     const func = decl.val.castTag(.function).?.data;
     const line_off = @intCast(u28, decl.src_line + func.lbrace_line);
 
-    const dwarf_segment = &self.load_commands.items[self.dwarf_segment_cmd_index.?].Segment;
+    const dwarf_segment = &self.load_commands.items[self.dwarf_segment_cmd_index.?].segment;
     const shdr = &dwarf_segment.sections.items[self.debug_line_section_index.?];
     const file_pos = shdr.offset + decl.fn_link.macho.off + getRelocDbgLineOff();
     var data: [4]u8 = undefined;
@@ -982,7 +1016,7 @@ pub fn commitDeclDebugInfo(
             // `TextBlock` and the .debug_info. If you are editing this logic, you
             // probably need to edit that logic too.
 
-            const dwarf_segment = &self.load_commands.items[self.dwarf_segment_cmd_index.?].Segment;
+            const dwarf_segment = &self.load_commands.items[self.dwarf_segment_cmd_index.?].segment;
             const debug_line_sect = &dwarf_segment.sections.items[self.debug_line_section_index.?];
             const src_fn = &decl.fn_link.macho;
             src_fn.len = @intCast(u32, dbg_line_buffer.items.len);
@@ -1028,8 +1062,8 @@ pub fn commitDeclDebugInfo(
             const last_src_fn = self.dbg_line_fn_last.?;
             const needed_size = last_src_fn.off + last_src_fn.len;
             if (needed_size != debug_line_sect.size) {
-                if (needed_size > dwarf_segment.allocatedSize(debug_line_sect.offset)) {
-                    const new_offset = dwarf_segment.findFreeSpace(needed_size, 1, null);
+                if (needed_size > self.allocatedSize(debug_line_sect.offset)) {
+                    const new_offset = self.findFreeSpace(needed_size, 1);
                     const existing_size = last_src_fn.off;
 
                     log.debug("moving __debug_line section: {} bytes from 0x{x} to 0x{x}", .{
@@ -1151,7 +1185,7 @@ fn updateDeclDebugInfoAllocation(
     // `SrcFn` and the line number programs. If you are editing this logic, you
     // probably need to edit that logic too.
 
-    const dwarf_segment = &self.load_commands.items[self.dwarf_segment_cmd_index.?].Segment;
+    const dwarf_segment = &self.load_commands.items[self.dwarf_segment_cmd_index.?].segment;
     const debug_info_sect = &dwarf_segment.sections.items[self.debug_info_section_index.?];
     text_block.dbg_info_len = len;
     if (self.dbg_info_decl_last) |last| blk: {
@@ -1202,15 +1236,15 @@ fn writeDeclDebugInfo(self: *DebugSymbols, text_block: *TextBlock, dbg_info_buf:
     // `SrcFn` and the line number programs. If you are editing this logic, you
     // probably need to edit that logic too.
 
-    const dwarf_segment = &self.load_commands.items[self.dwarf_segment_cmd_index.?].Segment;
+    const dwarf_segment = &self.load_commands.items[self.dwarf_segment_cmd_index.?].segment;
     const debug_info_sect = &dwarf_segment.sections.items[self.debug_info_section_index.?];
 
     const last_decl = self.dbg_info_decl_last.?;
     // +1 for a trailing zero to end the children of the decl tag.
     const needed_size = last_decl.dbg_info_off + last_decl.dbg_info_len + 1;
     if (needed_size != debug_info_sect.size) {
-        if (needed_size > dwarf_segment.allocatedSize(debug_info_sect.offset)) {
-            const new_offset = dwarf_segment.findFreeSpace(needed_size, 1, null);
+        if (needed_size > self.allocatedSize(debug_info_sect.offset)) {
+            const new_offset = self.findFreeSpace(needed_size, 1);
             const existing_size = last_decl.dbg_info_off;
 
             log.debug("moving __debug_info section: {} bytes from 0x{x} to 0x{x}", .{
src/link/MachO/Dylib.zig
@@ -9,11 +9,9 @@ const macho = std.macho;
 const math = std.math;
 const mem = std.mem;
 const fat = @import("fat.zig");
-const commands = @import("commands.zig");
 
 const Allocator = mem.Allocator;
 const LibStub = @import("../tapi.zig").LibStub;
-const LoadCommand = commands.LoadCommand;
 const MachO = @import("../MachO.zig");
 
 file: fs.File,
@@ -25,7 +23,7 @@ header: ?macho.mach_header_64 = null,
 // an offset within a file if we are linking against a fat lib
 library_offset: u64 = 0,
 
-load_commands: std.ArrayListUnmanaged(LoadCommand) = .{},
+load_commands: std.ArrayListUnmanaged(macho.LoadCommand) = .{},
 
 symtab_cmd_index: ?u16 = null,
 dysymtab_cmd_index: ?u16 = null,
@@ -53,7 +51,7 @@ pub const Id = struct {
         };
     }
 
-    pub fn fromLoadCommand(allocator: Allocator, lc: commands.GenericCommandWithData(macho.dylib_command)) !Id {
+    pub fn fromLoadCommand(allocator: Allocator, lc: macho.GenericCommandWithData(macho.dylib_command)) !Id {
         const dylib = lc.inner.dylib;
         const dylib_name = @ptrCast([*:0]const u8, lc.data[dylib.name - @sizeOf(macho.dylib_command) ..]);
         const name = try allocator.dupe(u8, mem.sliceTo(dylib_name, 0));
@@ -177,7 +175,7 @@ fn readLoadCommands(self: *Dylib, allocator: Allocator, reader: anytype, depende
 
     var i: u16 = 0;
     while (i < self.header.?.ncmds) : (i += 1) {
-        var cmd = try LoadCommand.read(allocator, reader);
+        var cmd = try macho.LoadCommand.read(allocator, reader);
         switch (cmd.cmd()) {
             macho.LC_SYMTAB => {
                 self.symtab_cmd_index = i;
@@ -191,7 +189,7 @@ fn readLoadCommands(self: *Dylib, allocator: Allocator, reader: anytype, depende
             macho.LC_REEXPORT_DYLIB => {
                 if (should_lookup_reexports) {
                     // Parse install_name to dependent dylib.
-                    var id = try Id.fromLoadCommand(allocator, cmd.Dylib);
+                    var id = try Id.fromLoadCommand(allocator, cmd.dylib);
                     try dependent_libs.writeItem(id);
                 }
             },
@@ -209,12 +207,12 @@ fn parseId(self: *Dylib, allocator: Allocator) !void {
         self.id = try Id.default(allocator, self.name);
         return;
     };
-    self.id = try Id.fromLoadCommand(allocator, self.load_commands.items[index].Dylib);
+    self.id = try Id.fromLoadCommand(allocator, self.load_commands.items[index].dylib);
 }
 
 fn parseSymbols(self: *Dylib, allocator: Allocator) !void {
     const index = self.symtab_cmd_index orelse return;
-    const symtab_cmd = self.load_commands.items[index].Symtab;
+    const symtab_cmd = self.load_commands.items[index].symtab;
 
     var symtab = try allocator.alloc(u8, @sizeOf(macho.nlist_64) * symtab_cmd.nsyms);
     defer allocator.free(symtab);
src/link/MachO/Object.zig
@@ -15,7 +15,6 @@ const trace = @import("../../tracy.zig").trace;
 
 const Allocator = mem.Allocator;
 const Atom = @import("Atom.zig");
-const LoadCommand = @import("commands.zig").LoadCommand;
 const MachO = @import("../MachO.zig");
 
 file: fs.File,
@@ -25,7 +24,7 @@ file_offset: ?u32 = null,
 
 header: ?macho.mach_header_64 = null,
 
-load_commands: std.ArrayListUnmanaged(LoadCommand) = .{},
+load_commands: std.ArrayListUnmanaged(macho.LoadCommand) = .{},
 
 segment_cmd_index: ?u16 = null,
 text_section_index: ?u16 = null,
@@ -268,11 +267,11 @@ pub fn readLoadCommands(self: *Object, allocator: Allocator, reader: anytype) !v
 
     var i: u16 = 0;
     while (i < header.ncmds) : (i += 1) {
-        var cmd = try LoadCommand.read(allocator, reader);
+        var cmd = try macho.LoadCommand.read(allocator, reader);
         switch (cmd.cmd()) {
             macho.LC_SEGMENT_64 => {
                 self.segment_cmd_index = i;
-                var seg = cmd.Segment;
+                var seg = cmd.segment;
                 for (seg.sections.items) |*sect, j| {
                     const index = @intCast(u16, j);
                     const segname = sect.segName();
@@ -305,8 +304,8 @@ pub fn readLoadCommands(self: *Object, allocator: Allocator, reader: anytype) !v
             },
             macho.LC_SYMTAB => {
                 self.symtab_cmd_index = i;
-                cmd.Symtab.symoff += offset;
-                cmd.Symtab.stroff += offset;
+                cmd.symtab.symoff += offset;
+                cmd.symtab.stroff += offset;
             },
             macho.LC_DYSYMTAB => {
                 self.dysymtab_cmd_index = i;
@@ -316,7 +315,7 @@ pub fn readLoadCommands(self: *Object, allocator: Allocator, reader: anytype) !v
             },
             macho.LC_DATA_IN_CODE => {
                 self.data_in_code_cmd_index = i;
-                cmd.LinkeditData.dataoff += offset;
+                cmd.linkedit_data.dataoff += offset;
             },
             else => {
                 log.debug("Unknown load command detected: 0x{x}.", .{cmd.cmd()});
@@ -382,7 +381,7 @@ pub fn parseIntoAtoms(self: *Object, allocator: Allocator, macho_file: *MachO) !
     const tracy = trace(@src());
     defer tracy.end();
 
-    const seg = self.load_commands.items[self.segment_cmd_index.?].Segment;
+    const seg = self.load_commands.items[self.segment_cmd_index.?].segment;
 
     log.debug("analysing {s}", .{self.name});
 
@@ -405,7 +404,7 @@ pub fn parseIntoAtoms(self: *Object, allocator: Allocator, macho_file: *MachO) !
     // Well, shit, sometimes compilers skip the dysymtab load command altogether, meaning we
     // have to infer the start of undef section in the symtab ourselves.
     const iundefsym = if (self.dysymtab_cmd_index) |cmd_index| blk: {
-        const dysymtab = self.load_commands.items[cmd_index].Dysymtab;
+        const dysymtab = self.load_commands.items[cmd_index].dysymtab;
         break :blk dysymtab.iundefsym;
     } else blk: {
         var iundefsym: usize = sorted_all_nlists.items.len;
@@ -553,7 +552,7 @@ pub fn parseIntoAtoms(self: *Object, allocator: Allocator, macho_file: *MachO) !
 
 fn parseSymtab(self: *Object, allocator: Allocator) !void {
     const index = self.symtab_cmd_index orelse return;
-    const symtab_cmd = self.load_commands.items[index].Symtab;
+    const symtab_cmd = self.load_commands.items[index].symtab;
 
     var symtab = try allocator.alloc(u8, @sizeOf(macho.nlist_64) * symtab_cmd.nsyms);
     defer allocator.free(symtab);
@@ -601,7 +600,7 @@ pub fn parseDebugInfo(self: *Object, allocator: Allocator) !void {
 
 pub fn parseDataInCode(self: *Object, allocator: Allocator) !void {
     const index = self.data_in_code_cmd_index orelse return;
-    const data_in_code = self.load_commands.items[index].LinkeditData;
+    const data_in_code = self.load_commands.items[index].linkedit_data;
 
     var buffer = try allocator.alloc(u8, data_in_code.datasize);
     defer allocator.free(buffer);
@@ -620,7 +619,7 @@ pub fn parseDataInCode(self: *Object, allocator: Allocator) !void {
 }
 
 fn readSection(self: Object, allocator: Allocator, index: u16) ![]u8 {
-    const seg = self.load_commands.items[self.segment_cmd_index.?].Segment;
+    const seg = self.load_commands.items[self.segment_cmd_index.?].segment;
     const sect = seg.sections.items[index];
     var buffer = try allocator.alloc(u8, @intCast(usize, sect.size));
     _ = try self.file.preadAll(buffer, sect.offset);
src/link/MachO.zig
@@ -15,7 +15,6 @@ const meta = std.meta;
 const aarch64 = @import("../arch/aarch64/bits.zig");
 const bind = @import("MachO/bind.zig");
 const codegen = @import("../codegen.zig");
-const commands = @import("MachO/commands.zig");
 const link = @import("../link.zig");
 const llvm_backend = @import("../codegen/llvm.zig");
 const target_util = @import("../target.zig");
@@ -35,9 +34,7 @@ const Object = @import("MachO/Object.zig");
 const LibStub = @import("tapi.zig").LibStub;
 const Liveness = @import("../Liveness.zig");
 const LlvmObject = @import("../codegen/llvm.zig").Object;
-const LoadCommand = commands.LoadCommand;
 const Module = @import("../Module.zig");
-const SegmentCommand = commands.SegmentCommand;
 const StringIndexAdapter = std.hash_map.StringIndexAdapter;
 const StringIndexContext = std.hash_map.StringIndexContext;
 const Trie = @import("MachO/Trie.zig");
@@ -83,7 +80,7 @@ dylibs: std.ArrayListUnmanaged(Dylib) = .{},
 dylibs_map: std.StringHashMapUnmanaged(u16) = .{},
 referenced_dylibs: std.AutoArrayHashMapUnmanaged(u16, void) = .{},
 
-load_commands: std.ArrayListUnmanaged(LoadCommand) = .{},
+load_commands: std.ArrayListUnmanaged(macho.LoadCommand) = .{},
 
 pagezero_segment_cmd_index: ?u16 = null,
 text_segment_cmd_index: ?u16 = null,
@@ -783,7 +780,7 @@ pub fn flushModule(self: *MachO, comp: *Compilation) !void {
                     @sizeOf(macho.rpath_command) + rpath.len + 1,
                     @sizeOf(u64),
                 ));
-                var rpath_cmd = commands.emptyGenericCommandWithData(macho.rpath_command{
+                var rpath_cmd = macho.emptyGenericCommandWithData(macho.rpath_command{
                     .cmd = macho.LC_RPATH,
                     .cmdsize = cmdsize,
                     .path = @sizeOf(macho.rpath_command),
@@ -791,7 +788,7 @@ pub fn flushModule(self: *MachO, comp: *Compilation) !void {
                 rpath_cmd.data = try self.base.allocator.alloc(u8, cmdsize - rpath_cmd.inner.path);
                 mem.set(u8, rpath_cmd.data, 0);
                 mem.copy(u8, rpath_cmd.data, rpath);
-                try self.load_commands.append(self.base.allocator, .{ .Rpath = rpath_cmd });
+                try self.load_commands.append(self.base.allocator, .{ .rpath = rpath_cmd });
                 try rpath_table.putNoClobber(rpath, {});
                 self.load_commands_dirty = true;
             }
@@ -861,12 +858,12 @@ pub fn flushModule(self: *MachO, comp: *Compilation) !void {
         }
 
         if (self.bss_section_index) |idx| {
-            const seg = &self.load_commands.items[self.data_segment_cmd_index.?].Segment;
+            const seg = &self.load_commands.items[self.data_segment_cmd_index.?].segment;
             const sect = &seg.sections.items[idx];
             sect.offset = self.bss_file_offset;
         }
         if (self.tlv_bss_section_index) |idx| {
-            const seg = &self.load_commands.items[self.data_segment_cmd_index.?].Segment;
+            const seg = &self.load_commands.items[self.data_segment_cmd_index.?].segment;
             const sect = &seg.sections.items[idx];
             sect.offset = self.tlv_bss_file_offset;
         }
@@ -942,13 +939,13 @@ pub fn flushModule(self: *MachO, comp: *Compilation) !void {
         }
 
         if (self.bss_section_index) |idx| {
-            const seg = &self.load_commands.items[self.data_segment_cmd_index.?].Segment;
+            const seg = &self.load_commands.items[self.data_segment_cmd_index.?].segment;
             const sect = &seg.sections.items[idx];
             self.bss_file_offset = sect.offset;
             sect.offset = 0;
         }
         if (self.tlv_bss_section_index) |idx| {
-            const seg = &self.load_commands.items[self.data_segment_cmd_index.?].Segment;
+            const seg = &self.load_commands.items[self.data_segment_cmd_index.?].segment;
             const sect = &seg.sections.items[idx];
             self.tlv_bss_file_offset = sect.offset;
             sect.offset = 0;
@@ -1865,7 +1862,7 @@ pub fn createEmptyAtom(self: *MachO, local_sym_index: u32, size: u64, alignment:
 }
 
 pub fn writeAtom(self: *MachO, atom: *Atom, match: MatchingSection) !void {
-    const seg = self.load_commands.items[match.seg].Segment;
+    const seg = self.load_commands.items[match.seg].segment;
     const sect = seg.sections.items[match.sect];
     const sym = self.locals.items[atom.local_sym_index];
     const file_offset = sect.offset + sym.n_value - sect.addr;
@@ -1885,7 +1882,7 @@ fn allocateLocals(self: *MachO) !void {
         }
 
         const n_sect = @intCast(u8, self.section_ordinals.getIndex(match).? + 1);
-        const seg = self.load_commands.items[match.seg].Segment;
+        const seg = self.load_commands.items[match.seg].segment;
         const sect = seg.sections.items[match.sect];
         var base_vaddr = sect.addr;
 
@@ -1976,7 +1973,7 @@ fn writeAllAtoms(self: *MachO) !void {
     var it = self.atoms.iterator();
     while (it.next()) |entry| {
         const match = entry.key_ptr.*;
-        const seg = self.load_commands.items[match.seg].Segment;
+        const seg = self.load_commands.items[match.seg].segment;
         const sect = seg.sections.items[match.sect];
         var atom: *Atom = entry.value_ptr.*;
 
@@ -2028,7 +2025,7 @@ fn writeAtoms(self: *MachO) !void {
     var it = self.atoms.iterator();
     while (it.next()) |entry| {
         const match = entry.key_ptr.*;
-        const seg = self.load_commands.items[match.seg].Segment;
+        const seg = self.load_commands.items[match.seg].segment;
         const sect = seg.sections.items[match.sect];
         var atom: *Atom = entry.value_ptr.*;
 
@@ -2992,7 +2989,7 @@ fn parseObjectsIntoAtoms(self: *MachO) !void {
 
             const first_atom = atom;
 
-            const seg = self.load_commands.items[match.seg].Segment;
+            const seg = self.load_commands.items[match.seg].segment;
             const sect = seg.sections.items[match.sect];
             const metadata = try section_metadata.getOrPut(match);
             if (!metadata.found_existing) {
@@ -3043,7 +3040,7 @@ fn parseObjectsIntoAtoms(self: *MachO) !void {
     while (it.next()) |entry| {
         const match = entry.key_ptr.*;
         const metadata = entry.value_ptr.*;
-        const seg = &self.load_commands.items[match.seg].Segment;
+        const seg = &self.load_commands.items[match.seg].segment;
         const sect = &seg.sections.items[match.sect];
         log.debug("{s},{s} => size: 0x{x}, alignment: 0x{x}", .{
             sect.segName(),
@@ -3067,7 +3064,7 @@ fn parseObjectsIntoAtoms(self: *MachO) !void {
         self.data_segment_cmd_index,
     }) |maybe_seg_id| {
         const seg_id = maybe_seg_id orelse continue;
-        const seg = self.load_commands.items[seg_id].Segment;
+        const seg = self.load_commands.items[seg_id].segment;
 
         for (seg.sections.items) |sect, sect_id| {
             const match = MatchingSection{
@@ -3137,7 +3134,7 @@ fn parseObjectsIntoAtoms(self: *MachO) !void {
 fn addLoadDylibLC(self: *MachO, id: u16) !void {
     const dylib = self.dylibs.items[id];
     const dylib_id = dylib.id orelse unreachable;
-    var dylib_cmd = try commands.createLoadDylibCommand(
+    var dylib_cmd = try macho.createLoadDylibCommand(
         self.base.allocator,
         dylib_id.name,
         dylib_id.timestamp,
@@ -3145,7 +3142,7 @@ fn addLoadDylibLC(self: *MachO, id: u16) !void {
         dylib_id.compatibility_version,
     );
     errdefer dylib_cmd.deinit(self.base.allocator);
-    try self.load_commands.append(self.base.allocator, .{ .Dylib = dylib_cmd });
+    try self.load_commands.append(self.base.allocator, .{ .dylib = dylib_cmd });
     self.load_commands_dirty = true;
 }
 
@@ -3153,7 +3150,7 @@ fn addCodeSignatureLC(self: *MachO) !void {
     if (self.code_signature_cmd_index != null or !self.requires_adhoc_codesig) return;
     self.code_signature_cmd_index = @intCast(u16, self.load_commands.items.len);
     try self.load_commands.append(self.base.allocator, .{
-        .LinkeditData = .{
+        .linkedit_data = .{
             .cmd = macho.LC_CODE_SIGNATURE,
             .cmdsize = @sizeOf(macho.linkedit_data_command),
             .dataoff = 0,
@@ -3168,7 +3165,7 @@ fn setEntryPoint(self: *MachO) !void {
 
     // TODO we should respect the -entry flag passed in by the user to set a custom
     // entrypoint. For now, assume default of `_main`.
-    const seg = self.load_commands.items[self.text_segment_cmd_index.?].Segment;
+    const seg = self.load_commands.items[self.text_segment_cmd_index.?].segment;
     const n_strx = self.strtab_dir.getKeyAdapted(@as([]const u8, "_main"), StringIndexAdapter{
         .bytes = &self.strtab,
     }) orelse {
@@ -3178,7 +3175,7 @@ fn setEntryPoint(self: *MachO) !void {
     const resolv = self.symbol_resolver.get(n_strx) orelse unreachable;
     assert(resolv.where == .global);
     const sym = self.globals.items[resolv.where_index];
-    const ec = &self.load_commands.items[self.main_cmd_index.?].Main;
+    const ec = &self.load_commands.items[self.main_cmd_index.?].main;
     ec.entryoff = @intCast(u32, sym.n_value - seg.inner.vmaddr);
     ec.stacksize = self.base.options.stack_size_override orelse 0;
     self.entry_addr = sym.n_value;
@@ -3875,7 +3872,7 @@ fn populateMissingMetadata(self: *MachO) !void {
     if (self.pagezero_segment_cmd_index == null) {
         self.pagezero_segment_cmd_index = @intCast(u16, self.load_commands.items.len);
         try self.load_commands.append(self.base.allocator, .{
-            .Segment = .{
+            .segment = .{
                 .inner = .{
                     .segname = makeStaticString("__PAGEZERO"),
                     .vmsize = pagezero_vmsize,
@@ -3896,7 +3893,7 @@ fn populateMissingMetadata(self: *MachO) !void {
             break :blk needed_size;
         } else 0;
         try self.load_commands.append(self.base.allocator, .{
-            .Segment = .{
+            .segment = .{
                 .inner = .{
                     .segname = makeStaticString("__TEXT"),
                     .vmaddr = pagezero_vmsize,
@@ -4000,7 +3997,7 @@ fn populateMissingMetadata(self: *MachO) !void {
             });
         }
         try self.load_commands.append(self.base.allocator, .{
-            .Segment = .{
+            .segment = .{
                 .inner = .{
                     .segname = makeStaticString("__DATA_CONST"),
                     .vmaddr = vmaddr,
@@ -4049,7 +4046,7 @@ fn populateMissingMetadata(self: *MachO) !void {
             });
         }
         try self.load_commands.append(self.base.allocator, .{
-            .Segment = .{
+            .segment = .{
                 .inner = .{
                     .segname = makeStaticString("__DATA"),
                     .vmaddr = vmaddr,
@@ -4133,7 +4130,7 @@ fn populateMissingMetadata(self: *MachO) !void {
                 .flags = macho.S_THREAD_LOCAL_ZEROFILL,
             },
         );
-        const seg = self.load_commands.items[self.data_segment_cmd_index.?].Segment;
+        const seg = self.load_commands.items[self.data_segment_cmd_index.?].segment;
         const sect = seg.sections.items[self.tlv_bss_section_index.?];
         self.tlv_bss_file_offset = sect.offset;
     }
@@ -4150,7 +4147,7 @@ fn populateMissingMetadata(self: *MachO) !void {
                 .flags = macho.S_ZEROFILL,
             },
         );
-        const seg = self.load_commands.items[self.data_segment_cmd_index.?].Segment;
+        const seg = self.load_commands.items[self.data_segment_cmd_index.?].segment;
         const sect = seg.sections.items[self.bss_section_index.?];
         self.bss_file_offset = sect.offset;
     }
@@ -4166,7 +4163,7 @@ fn populateMissingMetadata(self: *MachO) !void {
             log.debug("found __LINKEDIT segment free space at 0x{x}", .{fileoff});
         }
         try self.load_commands.append(self.base.allocator, .{
-            .Segment = .{
+            .segment = .{
                 .inner = .{
                     .segname = makeStaticString("__LINKEDIT"),
                     .vmaddr = vmaddr,
@@ -4182,7 +4179,7 @@ fn populateMissingMetadata(self: *MachO) !void {
     if (self.dyld_info_cmd_index == null) {
         self.dyld_info_cmd_index = @intCast(u16, self.load_commands.items.len);
         try self.load_commands.append(self.base.allocator, .{
-            .DyldInfoOnly = .{
+            .dyld_info_only = .{
                 .cmd = macho.LC_DYLD_INFO_ONLY,
                 .cmdsize = @sizeOf(macho.dyld_info_command),
                 .rebase_off = 0,
@@ -4203,7 +4200,7 @@ fn populateMissingMetadata(self: *MachO) !void {
     if (self.symtab_cmd_index == null) {
         self.symtab_cmd_index = @intCast(u16, self.load_commands.items.len);
         try self.load_commands.append(self.base.allocator, .{
-            .Symtab = .{
+            .symtab = .{
                 .cmd = macho.LC_SYMTAB,
                 .cmdsize = @sizeOf(macho.symtab_command),
                 .symoff = 0,
@@ -4218,7 +4215,7 @@ fn populateMissingMetadata(self: *MachO) !void {
     if (self.dysymtab_cmd_index == null) {
         self.dysymtab_cmd_index = @intCast(u16, self.load_commands.items.len);
         try self.load_commands.append(self.base.allocator, .{
-            .Dysymtab = .{
+            .dysymtab = .{
                 .cmd = macho.LC_DYSYMTAB,
                 .cmdsize = @sizeOf(macho.dysymtab_command),
                 .ilocalsym = 0,
@@ -4251,7 +4248,7 @@ fn populateMissingMetadata(self: *MachO) !void {
             @sizeOf(macho.dylinker_command) + mem.sliceTo(default_dyld_path, 0).len,
             @sizeOf(u64),
         ));
-        var dylinker_cmd = commands.emptyGenericCommandWithData(macho.dylinker_command{
+        var dylinker_cmd = macho.emptyGenericCommandWithData(macho.dylinker_command{
             .cmd = macho.LC_LOAD_DYLINKER,
             .cmdsize = cmdsize,
             .name = @sizeOf(macho.dylinker_command),
@@ -4259,14 +4256,14 @@ fn populateMissingMetadata(self: *MachO) !void {
         dylinker_cmd.data = try self.base.allocator.alloc(u8, cmdsize - dylinker_cmd.inner.name);
         mem.set(u8, dylinker_cmd.data, 0);
         mem.copy(u8, dylinker_cmd.data, mem.sliceTo(default_dyld_path, 0));
-        try self.load_commands.append(self.base.allocator, .{ .Dylinker = dylinker_cmd });
+        try self.load_commands.append(self.base.allocator, .{ .dylinker = dylinker_cmd });
         self.load_commands_dirty = true;
     }
 
     if (self.main_cmd_index == null and self.base.options.output_mode == .Exe) {
         self.main_cmd_index = @intCast(u16, self.load_commands.items.len);
         try self.load_commands.append(self.base.allocator, .{
-            .Main = .{
+            .main = .{
                 .cmd = macho.LC_MAIN,
                 .cmdsize = @sizeOf(macho.entry_point_command),
                 .entryoff = 0x0,
@@ -4286,7 +4283,7 @@ fn populateMissingMetadata(self: *MachO) !void {
             std.builtin.Version{ .major = 1, .minor = 0, .patch = 0 };
         const compat_version = self.base.options.compatibility_version orelse
             std.builtin.Version{ .major = 1, .minor = 0, .patch = 0 };
-        var dylib_cmd = try commands.createLoadDylibCommand(
+        var dylib_cmd = try macho.createLoadDylibCommand(
             self.base.allocator,
             install_name,
             2,
@@ -4295,14 +4292,14 @@ fn populateMissingMetadata(self: *MachO) !void {
         );
         errdefer dylib_cmd.deinit(self.base.allocator);
         dylib_cmd.inner.cmd = macho.LC_ID_DYLIB;
-        try self.load_commands.append(self.base.allocator, .{ .Dylib = dylib_cmd });
+        try self.load_commands.append(self.base.allocator, .{ .dylib = dylib_cmd });
         self.load_commands_dirty = true;
     }
 
     if (self.source_version_cmd_index == null) {
         self.source_version_cmd_index = @intCast(u16, self.load_commands.items.len);
         try self.load_commands.append(self.base.allocator, .{
-            .SourceVersion = .{
+            .source_version = .{
                 .cmd = macho.LC_SOURCE_VERSION,
                 .cmdsize = @sizeOf(macho.source_version_command),
                 .version = 0x0,
@@ -4329,7 +4326,7 @@ fn populateMissingMetadata(self: *MachO) !void {
             break :blk sdk_version;
         } else platform_version;
         const is_simulator_abi = self.base.options.target.abi == .simulator;
-        var cmd = commands.emptyGenericCommandWithData(macho.build_version_command{
+        var cmd = macho.emptyGenericCommandWithData(macho.build_version_command{
             .cmd = macho.LC_BUILD_VERSION,
             .cmdsize = cmdsize,
             .platform = switch (self.base.options.target.os.tag) {
@@ -4350,7 +4347,7 @@ fn populateMissingMetadata(self: *MachO) !void {
         cmd.data = try self.base.allocator.alloc(u8, cmdsize - @sizeOf(macho.build_version_command));
         mem.set(u8, cmd.data, 0);
         mem.copy(u8, cmd.data, mem.asBytes(&ld_ver));
-        try self.load_commands.append(self.base.allocator, .{ .BuildVersion = cmd });
+        try self.load_commands.append(self.base.allocator, .{ .build_version = cmd });
         self.load_commands_dirty = true;
     }
 
@@ -4362,14 +4359,14 @@ fn populateMissingMetadata(self: *MachO) !void {
             .uuid = undefined,
         };
         std.crypto.random.bytes(&uuid_cmd.uuid);
-        try self.load_commands.append(self.base.allocator, .{ .Uuid = uuid_cmd });
+        try self.load_commands.append(self.base.allocator, .{ .uuid = uuid_cmd });
         self.load_commands_dirty = true;
     }
 
     if (self.function_starts_cmd_index == null) {
         self.function_starts_cmd_index = @intCast(u16, self.load_commands.items.len);
         try self.load_commands.append(self.base.allocator, .{
-            .LinkeditData = .{
+            .linkedit_data = .{
                 .cmd = macho.LC_FUNCTION_STARTS,
                 .cmdsize = @sizeOf(macho.linkedit_data_command),
                 .dataoff = 0,
@@ -4382,7 +4379,7 @@ fn populateMissingMetadata(self: *MachO) !void {
     if (self.data_in_code_cmd_index == null) {
         self.data_in_code_cmd_index = @intCast(u16, self.load_commands.items.len);
         try self.load_commands.append(self.base.allocator, .{
-            .LinkeditData = .{
+            .linkedit_data = .{
                 .cmd = macho.LC_DATA_IN_CODE,
                 .cmdsize = @sizeOf(macho.linkedit_data_command),
                 .dataoff = 0,
@@ -4396,8 +4393,8 @@ fn populateMissingMetadata(self: *MachO) !void {
 }
 
 fn allocateTextSegment(self: *MachO) !void {
-    const seg = &self.load_commands.items[self.text_segment_cmd_index.?].Segment;
-    const base_vmaddr = self.load_commands.items[self.pagezero_segment_cmd_index.?].Segment.inner.vmsize;
+    const seg = &self.load_commands.items[self.text_segment_cmd_index.?].segment;
+    const base_vmaddr = self.load_commands.items[self.pagezero_segment_cmd_index.?].segment.inner.vmsize;
     seg.inner.fileoff = 0;
     seg.inner.vmaddr = base_vmaddr;
 
@@ -4433,30 +4430,30 @@ fn allocateTextSegment(self: *MachO) !void {
 }
 
 fn allocateDataConstSegment(self: *MachO) !void {
-    const seg = &self.load_commands.items[self.data_const_segment_cmd_index.?].Segment;
-    const text_seg = self.load_commands.items[self.text_segment_cmd_index.?].Segment;
+    const seg = &self.load_commands.items[self.data_const_segment_cmd_index.?].segment;
+    const text_seg = self.load_commands.items[self.text_segment_cmd_index.?].segment;
     seg.inner.fileoff = text_seg.inner.fileoff + text_seg.inner.filesize;
     seg.inner.vmaddr = text_seg.inner.vmaddr + text_seg.inner.vmsize;
     try self.allocateSegment(self.data_const_segment_cmd_index.?, 0);
 }
 
 fn allocateDataSegment(self: *MachO) !void {
-    const seg = &self.load_commands.items[self.data_segment_cmd_index.?].Segment;
-    const data_const_seg = self.load_commands.items[self.data_const_segment_cmd_index.?].Segment;
+    const seg = &self.load_commands.items[self.data_segment_cmd_index.?].segment;
+    const data_const_seg = self.load_commands.items[self.data_const_segment_cmd_index.?].segment;
     seg.inner.fileoff = data_const_seg.inner.fileoff + data_const_seg.inner.filesize;
     seg.inner.vmaddr = data_const_seg.inner.vmaddr + data_const_seg.inner.vmsize;
     try self.allocateSegment(self.data_segment_cmd_index.?, 0);
 }
 
 fn allocateLinkeditSegment(self: *MachO) void {
-    const seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment;
-    const data_seg = self.load_commands.items[self.data_segment_cmd_index.?].Segment;
+    const seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].segment;
+    const data_seg = self.load_commands.items[self.data_segment_cmd_index.?].segment;
     seg.inner.fileoff = data_seg.inner.fileoff + data_seg.inner.filesize;
     seg.inner.vmaddr = data_seg.inner.vmaddr + data_seg.inner.vmsize;
 }
 
 fn allocateSegment(self: *MachO, index: u16, offset: u64) !void {
-    const seg = &self.load_commands.items[index].Segment;
+    const seg = &self.load_commands.items[index].segment;
 
     // Allocate the sections according to their alignment at the beginning of the segment.
     var start: u64 = offset;
@@ -4488,7 +4485,7 @@ fn initSection(
     alignment: u32,
     opts: InitSectionOpts,
 ) !u16 {
-    const seg = &self.load_commands.items[segment_id].Segment;
+    const seg = &self.load_commands.items[segment_id].segment;
     var sect = macho.section_64{
         .sectname = makeStaticString(sectname),
         .segname = seg.inner.segname,
@@ -4532,7 +4529,7 @@ fn initSection(
 }
 
 fn findFreeSpace(self: MachO, segment_id: u16, alignment: u64, start: ?u64) u64 {
-    const seg = self.load_commands.items[segment_id].Segment;
+    const seg = self.load_commands.items[segment_id].segment;
     if (seg.sections.items.len == 0) {
         return if (start) |v| v else seg.inner.fileoff;
     }
@@ -4542,7 +4539,7 @@ fn findFreeSpace(self: MachO, segment_id: u16, alignment: u64, start: ?u64) u64
 }
 
 fn growSegment(self: *MachO, seg_id: u16, new_size: u64) !void {
-    const seg = &self.load_commands.items[seg_id].Segment;
+    const seg = &self.load_commands.items[seg_id].segment;
     const new_seg_size = mem.alignForwardGeneric(u64, new_size, self.page_size);
     assert(new_seg_size > seg.inner.filesize);
     const offset_amt = new_seg_size - seg.inner.filesize;
@@ -4564,13 +4561,13 @@ fn growSegment(self: *MachO, seg_id: u16, new_size: u64) !void {
     // TODO We should probably nop the expanded by distance, or put 0s.
 
     // TODO copyRangeAll doesn't automatically extend the file on macOS.
-    const ledit_seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment;
+    const ledit_seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].segment;
     const new_filesize = offset_amt + ledit_seg.inner.fileoff + ledit_seg.inner.filesize;
     try self.base.file.?.pwriteAll(&[_]u8{0}, new_filesize - 1);
 
     var next: usize = seg_id + 1;
     while (next < self.linkedit_segment_cmd_index.? + 1) : (next += 1) {
-        const next_seg = &self.load_commands.items[next].Segment;
+        const next_seg = &self.load_commands.items[next].segment;
         _ = try self.base.file.?.copyRangeAll(
             next_seg.inner.fileoff,
             self.base.file.?,
@@ -4613,7 +4610,7 @@ fn growSection(self: *MachO, match: MatchingSection, new_size: u32) !void {
     const tracy = trace(@src());
     defer tracy.end();
 
-    const seg = &self.load_commands.items[match.seg].Segment;
+    const seg = &self.load_commands.items[match.seg].segment;
     const sect = &seg.sections.items[match.sect];
 
     const alignment = try math.powi(u32, 2, sect.@"align");
@@ -4684,7 +4681,7 @@ fn growSection(self: *MachO, match: MatchingSection, new_size: u32) !void {
 }
 
 fn allocatedSize(self: MachO, segment_id: u16, start: u64) u64 {
-    const seg = self.load_commands.items[segment_id].Segment;
+    const seg = self.load_commands.items[segment_id].segment;
     assert(start >= seg.inner.fileoff);
     var min_pos: u64 = seg.inner.fileoff + seg.inner.filesize;
     if (start > min_pos) return 0;
@@ -4696,7 +4693,7 @@ fn allocatedSize(self: MachO, segment_id: u16, start: u64) u64 {
 }
 
 fn getSectionMaxAlignment(self: *MachO, segment_id: u16, start_sect_id: u16) !u32 {
-    const seg = self.load_commands.items[segment_id].Segment;
+    const seg = self.load_commands.items[segment_id].segment;
     var max_alignment: u32 = 1;
     var next = start_sect_id;
     while (next < seg.sections.items.len) : (next += 1) {
@@ -4711,7 +4708,7 @@ fn allocateAtom(self: *MachO, atom: *Atom, new_atom_size: u64, alignment: u64, m
     const tracy = trace(@src());
     defer tracy.end();
 
-    const seg = &self.load_commands.items[match.seg].Segment;
+    const seg = &self.load_commands.items[match.seg].segment;
     const sect = &seg.sections.items[match.sect];
     var free_list = self.atom_free_lists.get(match).?;
     const needs_padding = match.seg == self.text_segment_cmd_index.? and match.sect == self.text_section_index.?;
@@ -4815,7 +4812,7 @@ fn allocateAtom(self: *MachO, atom: *Atom, new_atom_size: u64, alignment: u64, m
 }
 
 fn addAtomAndBumpSectionSize(self: *MachO, atom: *Atom, match: MatchingSection) !void {
-    const seg = &self.load_commands.items[match.seg].Segment;
+    const seg = &self.load_commands.items[match.seg].segment;
     const sect = &seg.sections.items[match.sect];
     const alignment = try math.powi(u32, 2, atom.alignment);
     sect.size = mem.alignForwardGeneric(u64, sect.size, alignment) + atom.size;
@@ -4862,11 +4859,11 @@ const NextSegmentAddressAndOffset = struct {
 fn nextSegmentAddressAndOffset(self: *MachO) NextSegmentAddressAndOffset {
     var prev_segment_idx: ?usize = null; // We use optional here for safety.
     for (self.load_commands.items) |cmd, i| {
-        if (cmd == .Segment) {
+        if (cmd == .segment) {
             prev_segment_idx = i;
         }
     }
-    const prev_segment = self.load_commands.items[prev_segment_idx.?].Segment;
+    const prev_segment = self.load_commands.items[prev_segment_idx.?].segment;
     const address = prev_segment.inner.vmaddr + prev_segment.inner.vmsize;
     const offset = prev_segment.inner.fileoff + prev_segment.inner.filesize;
     return .{
@@ -4885,7 +4882,7 @@ fn sortSections(self: *MachO) !void {
 
     {
         // __TEXT segment
-        const seg = &self.load_commands.items[self.text_segment_cmd_index.?].Segment;
+        const seg = &self.load_commands.items[self.text_segment_cmd_index.?].segment;
         var sections = seg.sections.toOwnedSlice(self.base.allocator);
         defer self.base.allocator.free(sections);
         try seg.sections.ensureTotalCapacity(self.base.allocator, sections.len);
@@ -4917,7 +4914,7 @@ fn sortSections(self: *MachO) !void {
 
     {
         // __DATA_CONST segment
-        const seg = &self.load_commands.items[self.data_const_segment_cmd_index.?].Segment;
+        const seg = &self.load_commands.items[self.data_const_segment_cmd_index.?].segment;
         var sections = seg.sections.toOwnedSlice(self.base.allocator);
         defer self.base.allocator.free(sections);
         try seg.sections.ensureTotalCapacity(self.base.allocator, sections.len);
@@ -4944,7 +4941,7 @@ fn sortSections(self: *MachO) !void {
 
     {
         // __DATA segment
-        const seg = &self.load_commands.items[self.data_segment_cmd_index.?].Segment;
+        const seg = &self.load_commands.items[self.data_segment_cmd_index.?].segment;
         var sections = seg.sections.toOwnedSlice(self.base.allocator);
         defer self.base.allocator.free(sections);
         try seg.sections.ensureTotalCapacity(self.base.allocator, sections.len);
@@ -5000,7 +4997,7 @@ fn sortSections(self: *MachO) !void {
     {
         // Create new section ordinals.
         self.section_ordinals.clearRetainingCapacity();
-        const text_seg = self.load_commands.items[self.text_segment_cmd_index.?].Segment;
+        const text_seg = self.load_commands.items[self.text_segment_cmd_index.?].segment;
         for (text_seg.sections.items) |_, sect_id| {
             const res = self.section_ordinals.getOrPutAssumeCapacity(.{
                 .seg = self.text_segment_cmd_index.?,
@@ -5008,7 +5005,7 @@ fn sortSections(self: *MachO) !void {
             });
             assert(!res.found_existing);
         }
-        const data_const_seg = self.load_commands.items[self.data_const_segment_cmd_index.?].Segment;
+        const data_const_seg = self.load_commands.items[self.data_const_segment_cmd_index.?].segment;
         for (data_const_seg.sections.items) |_, sect_id| {
             const res = self.section_ordinals.getOrPutAssumeCapacity(.{
                 .seg = self.data_const_segment_cmd_index.?,
@@ -5016,7 +5013,7 @@ fn sortSections(self: *MachO) !void {
             });
             assert(!res.found_existing);
         }
-        const data_seg = self.load_commands.items[self.data_segment_cmd_index.?].Segment;
+        const data_seg = self.load_commands.items[self.data_segment_cmd_index.?].segment;
         for (data_seg.sections.items) |_, sect_id| {
             const res = self.section_ordinals.getOrPutAssumeCapacity(.{
                 .seg = self.data_segment_cmd_index.?,
@@ -5041,9 +5038,9 @@ fn updateSectionOrdinals(self: *MachO) !void {
 
     var new_ordinal: u8 = 0;
     for (self.load_commands.items) |lc, lc_id| {
-        if (lc != .Segment) break;
+        if (lc != .segment) break;
 
-        for (lc.Segment.sections.items) |_, sect_id| {
+        for (lc.segment.sections.items) |_, sect_id| {
             const match = MatchingSection{
                 .seg = @intCast(u16, lc_id),
                 .sect = @intCast(u16, sect_id),
@@ -5086,7 +5083,7 @@ fn writeDyldInfoData(self: *MachO) !void {
 
             if (match.seg == self.text_segment_cmd_index.?) continue; // __TEXT is non-writable
 
-            const seg = self.load_commands.items[match.seg].Segment;
+            const seg = self.load_commands.items[match.seg].segment;
 
             while (true) {
                 const sym = self.locals.items[atom.local_sym_index];
@@ -5156,7 +5153,7 @@ fn writeDyldInfoData(self: *MachO) !void {
     {
         // TODO handle macho.EXPORT_SYMBOL_FLAGS_REEXPORT and macho.EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER.
         log.debug("generating export trie", .{});
-        const text_segment = self.load_commands.items[self.text_segment_cmd_index.?].Segment;
+        const text_segment = self.load_commands.items[self.text_segment_cmd_index.?].segment;
         const base_address = text_segment.inner.vmaddr;
 
         for (self.globals.items) |sym| {
@@ -5174,8 +5171,8 @@ fn writeDyldInfoData(self: *MachO) !void {
         try trie.finalize(self.base.allocator);
     }
 
-    const seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment;
-    const dyld_info = &self.load_commands.items[self.dyld_info_cmd_index.?].DyldInfoOnly;
+    const seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].segment;
+    const dyld_info = &self.load_commands.items[self.dyld_info_cmd_index.?].dyld_info_only;
     const rebase_size = try bind.rebaseInfoSize(rebase_pointers.items);
     const bind_size = try bind.bindInfoSize(bind_pointers.items);
     const lazy_bind_size = try bind.lazyBindInfoSize(lazy_bind_pointers.items);
@@ -5245,7 +5242,7 @@ fn populateLazyBindOffsetsInStubHelper(self: *MachO, buffer: []const u8) !void {
             .sect = self.la_symbol_ptr_section_index.?,
         }).?;
         const base_addr = blk: {
-            const seg = self.load_commands.items[self.data_segment_cmd_index.?].Segment;
+            const seg = self.load_commands.items[self.data_segment_cmd_index.?].segment;
             break :blk seg.inner.vmaddr;
         };
 
@@ -5309,7 +5306,7 @@ fn populateLazyBindOffsetsInStubHelper(self: *MachO, buffer: []const u8) !void {
     }
 
     const sect = blk: {
-        const seg = self.load_commands.items[self.text_segment_cmd_index.?].Segment;
+        const seg = self.load_commands.items[self.text_segment_cmd_index.?].segment;
         break :blk seg.sections.items[self.stub_helper_section_index.?];
     };
     const stub_offset: u4 = switch (self.base.options.target.cpu.arch) {
@@ -5350,7 +5347,7 @@ fn writeFunctionStarts(self: *MachO) !void {
     var offsets = std.ArrayList(u32).init(self.base.allocator);
     defer offsets.deinit();
 
-    const text_seg = self.load_commands.items[self.text_segment_cmd_index.?].Segment;
+    const text_seg = self.load_commands.items[self.text_segment_cmd_index.?].segment;
     var last_off: u32 = 0;
 
     while (true) {
@@ -5407,8 +5404,8 @@ fn writeFunctionStarts(self: *MachO) !void {
     }
 
     const needed_size = @intCast(u32, mem.alignForwardGeneric(u64, stream.pos, @sizeOf(u64)));
-    const seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment;
-    const fn_cmd = &self.load_commands.items[self.function_starts_cmd_index.?].LinkeditData;
+    const seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].segment;
+    const fn_cmd = &self.load_commands.items[self.function_starts_cmd_index.?].linkedit_data;
 
     fn_cmd.dataoff = @intCast(u32, seg.inner.fileoff + seg.inner.filesize);
     fn_cmd.datasize = needed_size;
@@ -5441,7 +5438,7 @@ fn writeDices(self: *MachO) !void {
         atom = prev;
     }
 
-    const text_seg = self.load_commands.items[self.text_segment_cmd_index.?].Segment;
+    const text_seg = self.load_commands.items[self.text_segment_cmd_index.?].segment;
     const text_sect = text_seg.sections.items[self.text_section_index.?];
 
     while (true) {
@@ -5465,8 +5462,8 @@ fn writeDices(self: *MachO) !void {
         } else break;
     }
 
-    const seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment;
-    const dice_cmd = &self.load_commands.items[self.data_in_code_cmd_index.?].LinkeditData;
+    const seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].segment;
+    const dice_cmd = &self.load_commands.items[self.data_in_code_cmd_index.?].linkedit_data;
     const needed_size = @intCast(u32, buf.items.len);
 
     dice_cmd.dataoff = @intCast(u32, seg.inner.fileoff + seg.inner.filesize);
@@ -5486,8 +5483,8 @@ fn writeSymbolTable(self: *MachO) !void {
     const tracy = trace(@src());
     defer tracy.end();
 
-    const seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment;
-    const symtab = &self.load_commands.items[self.symtab_cmd_index.?].Symtab;
+    const seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].segment;
+    const symtab = &self.load_commands.items[self.symtab_cmd_index.?].symtab;
     symtab.symoff = @intCast(u32, seg.inner.fileoff + seg.inner.filesize);
 
     var locals = std.ArrayList(macho.nlist_64).init(self.base.allocator);
@@ -5591,18 +5588,18 @@ fn writeSymbolTable(self: *MachO) !void {
     seg.inner.filesize += locals_size + exports_size + undefs_size;
 
     // Update dynamic symbol table.
-    const dysymtab = &self.load_commands.items[self.dysymtab_cmd_index.?].Dysymtab;
+    const dysymtab = &self.load_commands.items[self.dysymtab_cmd_index.?].dysymtab;
     dysymtab.nlocalsym = @intCast(u32, nlocals);
     dysymtab.iextdefsym = dysymtab.nlocalsym;
     dysymtab.nextdefsym = @intCast(u32, nexports);
     dysymtab.iundefsym = dysymtab.nlocalsym + dysymtab.nextdefsym;
     dysymtab.nundefsym = @intCast(u32, nundefs);
 
-    const text_segment = &self.load_commands.items[self.text_segment_cmd_index.?].Segment;
+    const text_segment = &self.load_commands.items[self.text_segment_cmd_index.?].segment;
     const stubs = &text_segment.sections.items[self.stubs_section_index.?];
-    const data_const_segment = &self.load_commands.items[self.data_const_segment_cmd_index.?].Segment;
+    const data_const_segment = &self.load_commands.items[self.data_const_segment_cmd_index.?].segment;
     const got = &data_const_segment.sections.items[self.got_section_index.?];
-    const data_segment = &self.load_commands.items[self.data_segment_cmd_index.?].Segment;
+    const data_segment = &self.load_commands.items[self.data_segment_cmd_index.?].segment;
     const la_symbol_ptr = &data_segment.sections.items[self.la_symbol_ptr_section_index.?];
 
     const nstubs = @intCast(u32, self.stubs_map.keys().len);
@@ -5665,8 +5662,8 @@ fn writeStringTable(self: *MachO) !void {
     const tracy = trace(@src());
     defer tracy.end();
 
-    const seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment;
-    const symtab = &self.load_commands.items[self.symtab_cmd_index.?].Symtab;
+    const seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].segment;
+    const symtab = &self.load_commands.items[self.symtab_cmd_index.?].symtab;
     symtab.stroff = @intCast(u32, seg.inner.fileoff + seg.inner.filesize);
     symtab.strsize = @intCast(u32, mem.alignForwardGeneric(u64, self.strtab.items.len, @alignOf(u64)));
     seg.inner.filesize += symtab.strsize;
@@ -5686,7 +5683,7 @@ fn writeLinkeditSegment(self: *MachO) !void {
     const tracy = trace(@src());
     defer tracy.end();
 
-    const seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment;
+    const seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].segment;
     seg.inner.filesize = 0;
 
     try self.writeDyldInfoData();
@@ -5702,8 +5699,8 @@ fn writeCodeSignaturePadding(self: *MachO) !void {
     const tracy = trace(@src());
     defer tracy.end();
 
-    const linkedit_segment = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment;
-    const code_sig_cmd = &self.load_commands.items[self.code_signature_cmd_index.?].LinkeditData;
+    const linkedit_segment = &self.load_commands.items[self.linkedit_segment_cmd_index.?].segment;
+    const code_sig_cmd = &self.load_commands.items[self.code_signature_cmd_index.?].linkedit_data;
     const fileoff = linkedit_segment.inner.fileoff + linkedit_segment.inner.filesize;
     const needed_size = CodeSignature.calcCodeSignaturePaddingSize(
         self.base.options.emit.?.sub_path,
@@ -5729,8 +5726,8 @@ fn writeCodeSignature(self: *MachO) !void {
     const tracy = trace(@src());
     defer tracy.end();
 
-    const text_segment = self.load_commands.items[self.text_segment_cmd_index.?].Segment;
-    const code_sig_cmd = self.load_commands.items[self.code_signature_cmd_index.?].LinkeditData;
+    const text_segment = self.load_commands.items[self.text_segment_cmd_index.?].segment;
+    const code_sig_cmd = self.load_commands.items[self.code_signature_cmd_index.?].linkedit_data;
 
     var code_sig: CodeSignature = .{};
     defer code_sig.deinit(self.base.allocator);
@@ -5955,7 +5952,7 @@ fn snapshotState(self: *MachO) !void {
     var nodes = std.ArrayList(Snapshot.Node).init(arena);
 
     for (self.section_ordinals.keys()) |key| {
-        const seg = self.load_commands.items[key.seg].Segment;
+        const seg = self.load_commands.items[key.seg].segment;
         const sect = seg.sections.items[key.sect];
         const sect_name = try std.fmt.allocPrint(arena, "{s},{s}", .{ sect.segName(), sect.sectName() });
         try nodes.append(.{
@@ -6028,12 +6025,12 @@ fn snapshotState(self: *MachO) !void {
                             const is_tlv = is_tlv: {
                                 const source_sym = self.locals.items[atom.local_sym_index];
                                 const match = self.section_ordinals.keys()[source_sym.n_sect - 1];
-                                const match_seg = self.load_commands.items[match.seg].Segment;
+                                const match_seg = self.load_commands.items[match.seg].segment;
                                 const match_sect = match_seg.sections.items[match.sect];
                                 break :is_tlv match_sect.type_() == macho.S_THREAD_LOCAL_VARIABLES;
                             };
                             if (is_tlv) {
-                                const match_seg = self.load_commands.items[self.data_segment_cmd_index.?].Segment;
+                                const match_seg = self.load_commands.items[self.data_segment_cmd_index.?].segment;
                                 const base_address = inner: {
                                     if (self.tlv_data_section_index) |i| {
                                         break :inner match_seg.sections.items[i].addr;
@@ -6193,7 +6190,7 @@ fn logSymtab(self: MachO) void {
 
 fn logSectionOrdinals(self: MachO) void {
     for (self.section_ordinals.keys()) |match, i| {
-        const seg = self.load_commands.items[match.seg].Segment;
+        const seg = self.load_commands.items[match.seg].segment;
         const sect = seg.sections.items[match.sect];
         log.debug("ord {d}: {d},{d} => {s},{s}", .{
             i + 1,
CMakeLists.txt
@@ -590,7 +590,6 @@ set(ZIG_STAGE2_SOURCES
     "${CMAKE_SOURCE_DIR}/src/link/MachO/Object.zig"
     "${CMAKE_SOURCE_DIR}/src/link/MachO/Trie.zig"
     "${CMAKE_SOURCE_DIR}/src/link/MachO/bind.zig"
-    "${CMAKE_SOURCE_DIR}/src/link/MachO/commands.zig"
     "${CMAKE_SOURCE_DIR}/src/link/Plan9.zig"
     "${CMAKE_SOURCE_DIR}/src/link/Plan9/aout.zig"
     "${CMAKE_SOURCE_DIR}/src/link/Wasm.zig"