diff --git a/CMakeLists.txt b/CMakeLists.txt index c7ebfdfafccc..5206a9329220 100644 --- a/CMakeLists.txt +++ b/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" diff --git a/lib/std/macho.zig b/lib/std/macho.zig index dce6d10cd526..a77603c9efc3 100644 --- a/lib/std/macho.zig +++ b/lib/std/macho.zig @@ -1,3 +1,13 @@ +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, cputype: cpu_type_t, @@ -9,14 +19,14 @@ pub const mach_header = extern struct { }; pub const mach_header_64 = extern struct { - magic: u32, - cputype: cpu_type_t, - cpusubtype: cpu_subtype_t, - filetype: u32, - ncmds: u32, - sizeofcmds: u32, - flags: u32, - reserved: u32, + magic: u32 = MH_MAGIC_64, + cputype: cpu_type_t = 0, + cpusubtype: cpu_subtype_t = 0, + filetype: u32 = 0, + ncmds: u32 = 0, + sizeofcmds: u32 = 0, + flags: u32 = 0, + reserved: u32 = 0, }; pub const fat_header = extern struct { @@ -630,6 +640,10 @@ pub const segment_command_64 = extern struct { /// number of sections in segment nsects: u32 = 0, flags: u32 = 0, + + pub fn segName(seg: segment_command_64) []const u8 { + return parseName(&seg.segname); + } }; /// A segment is made up of zero or more sections. Non-MH_OBJECT files have @@ -728,8 +742,46 @@ pub const section_64 = extern struct { /// reserved reserved3: u32 = 0, + + pub fn sectName(sect: section_64) []const u8 { + return parseName(§.sectname); + } + + pub fn segName(sect: section_64) []const u8 { + return parseName(§.segname); + } + + pub fn type_(sect: section_64) u8 { + return @truncate(u8, sect.flags & 0xff); + } + + pub fn attrs(sect: section_64) u32 { + return sect.flags & 0xffffff00; + } + + pub fn isCode(sect: section_64) bool { + const attr = sect.attrs(); + return attr & S_ATTR_PURE_INSTRUCTIONS != 0 or attr & S_ATTR_SOME_INSTRUCTIONS != 0; + } + + pub fn isDebug(sect: section_64) bool { + return sect.attrs() & S_ATTR_DEBUG != 0; + } + + pub fn isDontDeadStrip(sect: section_64) bool { + return sect.attrs() & S_ATTR_NO_DEAD_STRIP != 0; + } + + pub fn isDontDeadStripIfReferencesLive(sect: section_64) bool { + return sect.attrs() & S_ATTR_LIVE_SUPPORT != 0; + } }; +fn parseName(name: *const [16]u8) []const u8 { + const len = mem.indexOfScalar(u8, name, @as(u8, 0)) orelse name.len; + return name[0..len]; +} + pub const nlist = extern struct { n_strx: u32, n_type: u8, @@ -1760,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(§)); + } + } + + 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); +} diff --git a/src/link/MachO.zig b/src/link/MachO.zig index 12562f8c5e0e..a67d3a4452d8 100644 --- a/src/link/MachO.zig +++ b/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; @@ -1324,10 +1321,10 @@ pub const MatchingSection = struct { }; pub fn getMatchingSection(self: *MachO, sect: macho.section_64) !?MatchingSection { - const segname = commands.segmentName(sect); - const sectname = commands.sectionName(sect); + const segname = sect.segName(); + const sectname = sect.sectName(); const res: ?MatchingSection = blk: { - switch (commands.sectionType(sect)) { + switch (sect.type_()) { macho.S_4BYTE_LITERALS, macho.S_8BYTE_LITERALS, macho.S_16BYTE_LITERALS => { if (self.text_const_section_index == null) { self.text_const_section_index = try self.initSection( @@ -1579,7 +1576,7 @@ pub fn getMatchingSection(self: *MachO, sect: macho.section_64) !?MatchingSectio }; }, macho.S_REGULAR => { - if (commands.sectionIsCode(sect)) { + if (sect.isCode()) { if (self.text_section_index == null) { self.text_section_index = try self.initSection( self.text_segment_cmd_index.?, @@ -1599,7 +1596,7 @@ pub fn getMatchingSection(self: *MachO, sect: macho.section_64) !?MatchingSectio .sect = self.text_section_index.?, }; } - if (commands.sectionIsDebug(sect)) { + if (sect.isDebug()) { // TODO debug attributes if (mem.eql(u8, "__LD", segname) and mem.eql(u8, "__compact_unwind", sectname)) { log.debug("TODO compact unwind section: type 0x{x}, name '{s},{s}'", .{ @@ -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,14 +1882,11 @@ 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; - log.debug("allocating local symbols in {s},{s}", .{ - commands.segmentName(sect), - commands.sectionName(sect), - }); + log.debug("allocating local symbols in {s},{s}", .{ sect.segName(), sect.sectName() }); while (true) { const alignment = try math.powi(u32, 2, atom.alignment); @@ -1979,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.*; @@ -1987,7 +1981,7 @@ fn writeAllAtoms(self: *MachO) !void { defer buffer.deinit(); try buffer.ensureTotalCapacity(try math.cast(usize, sect.size)); - log.debug("writing atoms in {s},{s}", .{ commands.segmentName(sect), commands.sectionName(sect) }); + log.debug("writing atoms in {s},{s}", .{ sect.segName(), sect.sectName() }); while (atom.prev) |prev| { atom = prev; @@ -2031,11 +2025,11 @@ 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.*; - log.debug("writing atoms in {s},{s}", .{ commands.segmentName(sect), commands.sectionName(sect) }); + log.debug("writing atoms in {s},{s}", .{ sect.segName(), sect.sectName() }); while (atom.prev) |prev| { atom = prev; @@ -2995,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) { @@ -3005,7 +2999,7 @@ fn parseObjectsIntoAtoms(self: *MachO) !void { }; } - log.debug("{s},{s}", .{ commands.segmentName(sect), commands.sectionName(sect) }); + log.debug("{s},{s}", .{ sect.segName(), sect.sectName() }); while (true) { const alignment = try math.powi(u32, 2, atom.alignment); @@ -3046,11 +3040,11 @@ 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}", .{ - commands.segmentName(sect.*), - commands.sectionName(sect.*), + sect.segName(), + sect.sectName(), metadata.size, metadata.alignment, }); @@ -3070,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{ @@ -3140,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, @@ -3148,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; } @@ -3156,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, @@ -3171,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 { @@ -3181,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; @@ -3878,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, @@ -3899,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, @@ -4003,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, @@ -4052,7 +4046,7 @@ fn populateMissingMetadata(self: *MachO) !void { }); } try self.load_commands.append(self.base.allocator, .{ - .Segment = .{ + .segment = .{ .inner = .{ .segname = makeStaticString("__DATA"), .vmaddr = vmaddr, @@ -4136,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; } @@ -4153,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; } @@ -4169,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, @@ -4185,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, @@ -4206,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, @@ -4221,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, @@ -4254,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), @@ -4262,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, @@ -4289,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, @@ -4298,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, @@ -4332,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) { @@ -4353,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; } @@ -4365,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, @@ -4385,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, @@ -4399,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; @@ -4436,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; @@ -4491,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, @@ -4507,8 +4501,8 @@ fn initSection( const padding: ?u64 = if (segment_id == self.text_segment_cmd_index.?) self.header_pad else null; const off = self.findFreeSpace(segment_id, alignment_pow_2, padding); log.debug("allocating {s},{s} section from 0x{x} to 0x{x}", .{ - commands.segmentName(sect), - commands.sectionName(sect), + sect.segName(), + sect.sectName(), off, off + size, }); @@ -4535,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; } @@ -4545,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; @@ -4567,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.?, @@ -4596,8 +4590,8 @@ fn growSegment(self: *MachO, seg_id: u16, new_size: u64) !void { moved_sect.addr += offset_amt; log.debug(" (new {s},{s} file offsets from 0x{x} to 0x{x} (in memory 0x{x} to 0x{x}))", .{ - commands.segmentName(moved_sect.*), - commands.sectionName(moved_sect.*), + moved_sect.segName(), + moved_sect.sectName(), moved_sect.offset, moved_sect.offset + moved_sect.size, moved_sect.addr, @@ -4616,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"); @@ -4670,8 +4664,8 @@ fn growSection(self: *MachO, match: MatchingSection, new_size: u32) !void { moved_sect.addr += offset_amt; log.debug(" (new {s},{s} file offsets from 0x{x} to 0x{x} (in memory 0x{x} to 0x{x}))", .{ - commands.segmentName(moved_sect.*), - commands.sectionName(moved_sect.*), + moved_sect.segName(), + moved_sect.sectName(), moved_sect.offset, moved_sect.offset + moved_sect.size, moved_sect.addr, @@ -4687,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; @@ -4699,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) { @@ -4714,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.?; @@ -4818,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; @@ -4865,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 .{ @@ -4888,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); @@ -4920,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); @@ -4947,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); @@ -5003,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.?, @@ -5011,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.?, @@ -5019,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.?, @@ -5044,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), @@ -5089,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]; @@ -5159,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| { @@ -5177,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); @@ -5248,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; }; @@ -5312,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) { @@ -5353,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) { @@ -5410,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; @@ -5444,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) { @@ -5468,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); @@ -5489,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); @@ -5594,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); @@ -5668,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; @@ -5689,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(); @@ -5705,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, @@ -5732,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); @@ -5784,9 +5778,8 @@ fn writeLoadCommands(self: *MachO) !void { /// Writes Mach-O file header. fn writeHeader(self: *MachO) !void { - var header = commands.emptyHeader(.{ - .flags = macho.MH_NOUNDEFS | macho.MH_DYLDLINK | macho.MH_PIE | macho.MH_TWOLEVEL, - }); + var header: macho.mach_header_64 = .{}; + header.flags = macho.MH_NOUNDEFS | macho.MH_DYLDLINK | macho.MH_PIE | macho.MH_TWOLEVEL; switch (self.base.options.target.cpu.arch) { .aarch64 => { @@ -5959,12 +5952,9 @@ 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}", .{ - commands.segmentName(sect), - commands.sectionName(sect), - }); + const sect_name = try std.fmt.allocPrint(arena, "{s},{s}", .{ sect.segName(), sect.sectName() }); try nodes.append(.{ .address = sect.addr, .tag = .section_start, @@ -6035,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 commands.sectionType(match_sect) == macho.S_THREAD_LOCAL_VARIABLES; + 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; @@ -6200,14 +6190,14 @@ 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, match.seg, match.sect, - commands.segmentName(sect), - commands.sectionName(sect), + sect.segName(), + sect.sectName(), }); } } diff --git a/src/link/MachO/Atom.zig b/src/link/MachO/Atom.zig index 99693a7da363..71183f41578b 100644 --- a/src/link/MachO/Atom.zig +++ b/src/link/MachO/Atom.zig @@ -4,7 +4,6 @@ const std = @import("std"); const build_options = @import("build_options"); const aarch64 = @import("../../arch/aarch64/bits.zig"); const assert = std.debug.assert; -const commands = @import("commands.zig"); const log = std.log.scoped(.link); const macho = std.macho; const math = std.math; @@ -342,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; @@ -398,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); } @@ -425,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); } @@ -447,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); @@ -490,9 +489,9 @@ 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 = commands.sectionType(sect); + const sect_type = sect.type_(); const should_rebase = rebase: { if (rel.r_length != 3) break :rebase false; @@ -705,9 +704,9 @@ 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 commands.sectionType(sect) == macho.S_THREAD_LOCAL_VARIABLES; + break :is_tlv sect.type_() == macho.S_THREAD_LOCAL_VARIABLES; }; if (is_tlv) { // For TLV relocations, the value specified as a relocation is the displacement from the @@ -715,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; diff --git a/src/link/MachO/DebugSymbols.zig b/src/link/MachO/DebugSymbols.zig index 9724a1ae033f..8babb5d64780 100644 --- a/src/link/MachO/DebugSymbols.zig +++ b/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,13 +233,13 @@ 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 log.debug("found {s},{s} section free space 0x{x} to 0x{x}", .{ - commands.segmentName(sect), - commands.sectionName(sect), + sect.segName(), + sect.sectName(), off, off + size, }); @@ -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; @@ -670,9 +693,8 @@ fn writeLoadCommands(self: *DebugSymbols, allocator: Allocator) !void { } fn writeHeader(self: *DebugSymbols) !void { - var header = commands.emptyHeader(.{ - .filetype = macho.MH_DSYM, - }); + var header: macho.mach_header_64 = .{}; + header.filetype = macho.MH_DSYM; switch (self.base.base.options.target.cpu.arch) { .aarch64 => { @@ -703,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; } @@ -711,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); @@ -748,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; @@ -781,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); @@ -793,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)); @@ -817,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; @@ -983,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); @@ -1029,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}", .{ @@ -1152,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: { @@ -1203,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}", .{ diff --git a/src/link/MachO/Dylib.zig b/src/link/MachO/Dylib.zig index 5f7bd9b7639a..31a8b357c2b2 100644 --- a/src/link/MachO/Dylib.zig +++ b/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); diff --git a/src/link/MachO/Object.zig b/src/link/MachO/Object.zig index 671019ff5146..7f4a49d0d501 100644 --- a/src/link/MachO/Object.zig +++ b/src/link/MachO/Object.zig @@ -11,14 +11,10 @@ const macho = std.macho; const math = std.math; const mem = std.mem; const sort = std.sort; -const commands = @import("commands.zig"); -const segmentName = commands.segmentName; -const sectionName = commands.sectionName; const trace = @import("../../tracy.zig").trace; const Allocator = mem.Allocator; const Atom = @import("Atom.zig"); -const LoadCommand = commands.LoadCommand; const MachO = @import("../MachO.zig"); file: fs.File, @@ -28,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, @@ -271,15 +267,15 @@ 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 = segmentName(sect.*); - const sectname = sectionName(sect.*); + const segname = sect.segName(); + const sectname = sect.sectName(); if (mem.eql(u8, segname, "__DWARF")) { if (mem.eql(u8, sectname, "__debug_info")) { self.dwarf_debug_info_index = index; @@ -308,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; @@ -319,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()}); @@ -385,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}); @@ -408,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; @@ -424,10 +420,7 @@ pub fn parseIntoAtoms(self: *Object, allocator: Allocator, macho_file: *MachO) ! for (seg.sections.items) |sect, id| { const sect_id = @intCast(u8, id); - log.debug("putting section '{s},{s}' as an Atom", .{ - segmentName(sect), - sectionName(sect), - }); + log.debug("putting section '{s},{s}' as an Atom", .{ sect.segName(), sect.sectName() }); // Get matching segment/section in the final artifact. const match = (try macho_file.getMatchingSection(sect)) orelse { @@ -479,7 +472,7 @@ pub fn parseIntoAtoms(self: *Object, allocator: Allocator, macho_file: *MachO) ! const atom = try macho_file.createEmptyAtom(atom_local_sym_index, aligned_size, sect.@"align"); const is_zerofill = blk: { - const section_type = commands.sectionType(sect); + const section_type = sect.type_(); break :blk section_type == macho.S_ZEROFILL or section_type == macho.S_THREAD_LOCAL_ZEROFILL; }; if (!is_zerofill) { @@ -559,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); @@ -607,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); @@ -626,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); diff --git a/src/link/MachO/commands.zig b/src/link/MachO/commands.zig deleted file mode 100644 index 41ea52b9df7f..000000000000 --- a/src/link/MachO/commands.zig +++ /dev/null @@ -1,523 +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 HeaderArgs = struct { - magic: u32 = macho.MH_MAGIC_64, - cputype: macho.cpu_type_t = 0, - cpusubtype: macho.cpu_subtype_t = 0, - filetype: u32 = 0, - flags: u32 = 0, - reserved: u32 = 0, -}; - -pub fn emptyHeader(args: HeaderArgs) macho.mach_header_64 { - return .{ - .magic = args.magic, - .cputype = args.cputype, - .cpusubtype = args.cpusubtype, - .filetype = args.filetype, - .ncmds = 0, - .sizeofcmds = 0, - .flags = args.flags, - .reserved = args.reserved, - }; -} - -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(§)); - } - } - - 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 parseName(name: *const [16]u8) []const u8 { - const len = mem.indexOfScalar(u8, name, @as(u8, 0)) orelse name.len; - return name[0..len]; -} - -pub fn segmentName(sect: macho.section_64) []const u8 { - return parseName(§.segname); -} - -pub fn sectionName(sect: macho.section_64) []const u8 { - return parseName(§.sectname); -} - -pub fn sectionType(sect: macho.section_64) u8 { - return @truncate(u8, sect.flags & 0xff); -} - -pub fn sectionAttrs(sect: macho.section_64) u32 { - return sect.flags & 0xffffff00; -} - -pub fn sectionIsCode(sect: macho.section_64) bool { - const attr = sectionAttrs(sect); - return attr & macho.S_ATTR_PURE_INSTRUCTIONS != 0 or attr & macho.S_ATTR_SOME_INSTRUCTIONS != 0; -} - -pub fn sectionIsDebug(sect: macho.section_64) bool { - return sectionAttrs(sect) & macho.S_ATTR_DEBUG != 0; -} - -pub fn sectionIsDontDeadStrip(sect: macho.section_64) bool { - return sectionAttrs(sect) & macho.S_ATTR_NO_DEAD_STRIP != 0; -} - -pub fn sectionIsDontDeadStripIfReferencesLive(sect: macho.section_64) bool { - return sectionAttrs(sect) & macho.S_ATTR_LIVE_SUPPORT != 0; -} - -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); -}