Merge pull request #10310 from ziglang/macho-common-functions

macho: move load command wrappers and parsing utils to std.macho
This commit is contained in:
Jakub Konka
2021-12-10 21:55:21 +01:00
committed by GitHub
8 changed files with 723 additions and 757 deletions

View File

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

View File

@@ -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(&sect.sectname);
}
pub fn segName(sect: section_64) []const u8 {
return parseName(&sect.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(&sect));
}
}
pub fn deinit(self: *SegmentCommand, allocator: Allocator) void {
self.sections.deinit(allocator);
}
pub fn eql(self: SegmentCommand, other: SegmentCommand) bool {
if (!meta.eql(self.inner, other.inner)) return false;
const lhs = self.sections.items;
const rhs = other.sections.items;
var i: usize = 0;
while (i < self.inner.nsects) : (i += 1) {
if (!meta.eql(lhs[i], rhs[i])) return false;
}
return true;
}
};
pub fn emptyGenericCommandWithData(cmd: anytype) GenericCommandWithData(@TypeOf(cmd)) {
return .{ .inner = cmd };
}
/// A Zig wrapper for a generic load command with variable-length data.
pub fn GenericCommandWithData(comptime Cmd: type) type {
return struct {
inner: Cmd,
/// This field remains undefined until `read` is called.
data: []u8 = undefined,
const Self = @This();
pub fn read(allocator: Allocator, reader: anytype) !Self {
const inner = try reader.readStruct(Cmd);
var data = try allocator.alloc(u8, inner.cmdsize - @sizeOf(Cmd));
errdefer allocator.free(data);
try reader.readNoEof(data);
return Self{
.inner = inner,
.data = data,
};
}
pub fn write(self: Self, writer: anytype) !void {
try writer.writeAll(mem.asBytes(&self.inner));
try writer.writeAll(self.data);
}
pub fn deinit(self: *Self, allocator: Allocator) void {
allocator.free(self.data);
}
pub fn eql(self: Self, other: Self) bool {
if (!meta.eql(self.inner, other.inner)) return false;
return mem.eql(u8, self.data, other.data);
}
};
}
pub fn createLoadDylibCommand(
allocator: Allocator,
name: []const u8,
timestamp: u32,
current_version: u32,
compatibility_version: u32,
) !GenericCommandWithData(dylib_command) {
const cmdsize = @intCast(u32, mem.alignForwardGeneric(
u64,
@sizeOf(dylib_command) + name.len + 1, // +1 for nul
@sizeOf(u64),
));
var dylib_cmd = emptyGenericCommandWithData(dylib_command{
.cmd = LC_LOAD_DYLIB,
.cmdsize = cmdsize,
.dylib = .{
.name = @sizeOf(dylib_command),
.timestamp = timestamp,
.current_version = current_version,
.compatibility_version = compatibility_version,
},
});
dylib_cmd.data = try allocator.alloc(u8, cmdsize - dylib_cmd.inner.dylib.name);
mem.set(u8, dylib_cmd.data, 0);
mem.copy(u8, dylib_cmd.data, name);
return dylib_cmd;
}
fn testRead(allocator: Allocator, buffer: []const u8, expected: anytype) !void {
var stream = io.fixedBufferStream(buffer);
var given = try LoadCommand.read(allocator, stream.reader());
defer given.deinit(allocator);
try testing.expect(expected.eql(given));
}
fn testWrite(buffer: []u8, cmd: LoadCommand, expected: []const u8) !void {
var stream = io.fixedBufferStream(buffer);
try cmd.write(stream.writer());
try testing.expect(mem.eql(u8, expected, buffer[0..expected.len]));
}
fn makeStaticString(bytes: []const u8) [16]u8 {
var buf = [_]u8{0} ** 16;
assert(bytes.len <= buf.len);
mem.copy(u8, &buf, bytes);
return buf;
}
test "read-write segment command" {
// TODO compiling for macOS from big-endian arch
if (builtin.target.cpu.arch.endian() != .Little) return error.SkipZigTest;
var gpa = testing.allocator;
const in_buffer = &[_]u8{
0x19, 0x00, 0x00, 0x00, // cmd
0x98, 0x00, 0x00, 0x00, // cmdsize
0x5f, 0x5f, 0x54, 0x45, 0x58, 0x54, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // segname
0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, // vmaddr
0x00, 0x80, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, // vmsize
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // fileoff
0x00, 0x80, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, // filesize
0x07, 0x00, 0x00, 0x00, // maxprot
0x05, 0x00, 0x00, 0x00, // initprot
0x01, 0x00, 0x00, 0x00, // nsects
0x00, 0x00, 0x00, 0x00, // flags
0x5f, 0x5f, 0x74, 0x65, 0x78, 0x74, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // sectname
0x5f, 0x5f, 0x54, 0x45, 0x58, 0x54, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // segname
0x00, 0x40, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, // address
0xc0, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // size
0x00, 0x40, 0x00, 0x00, // offset
0x02, 0x00, 0x00, 0x00, // alignment
0x00, 0x00, 0x00, 0x00, // reloff
0x00, 0x00, 0x00, 0x00, // nreloc
0x00, 0x04, 0x00, 0x80, // flags
0x00, 0x00, 0x00, 0x00, // reserved1
0x00, 0x00, 0x00, 0x00, // reserved2
0x00, 0x00, 0x00, 0x00, // reserved3
};
var cmd = SegmentCommand{
.inner = .{
.cmdsize = 152,
.segname = makeStaticString("__TEXT"),
.vmaddr = 4294967296,
.vmsize = 294912,
.filesize = 294912,
.maxprot = VM_PROT_READ | VM_PROT_WRITE | VM_PROT_EXECUTE,
.initprot = VM_PROT_EXECUTE | VM_PROT_READ,
.nsects = 1,
},
};
try cmd.sections.append(gpa, .{
.sectname = makeStaticString("__text"),
.segname = makeStaticString("__TEXT"),
.addr = 4294983680,
.size = 448,
.offset = 16384,
.@"align" = 2,
.flags = S_REGULAR | S_ATTR_PURE_INSTRUCTIONS | S_ATTR_SOME_INSTRUCTIONS,
});
defer cmd.deinit(gpa);
try testRead(gpa, in_buffer, LoadCommand{ .segment = cmd });
var out_buffer: [in_buffer.len]u8 = undefined;
try testWrite(&out_buffer, LoadCommand{ .segment = cmd }, in_buffer);
}
test "read-write generic command with data" {
// TODO compiling for macOS from big-endian arch
if (builtin.target.cpu.arch.endian() != .Little) return error.SkipZigTest;
var gpa = testing.allocator;
const in_buffer = &[_]u8{
0x0c, 0x00, 0x00, 0x00, // cmd
0x20, 0x00, 0x00, 0x00, // cmdsize
0x18, 0x00, 0x00, 0x00, // name
0x02, 0x00, 0x00, 0x00, // timestamp
0x00, 0x00, 0x00, 0x00, // current_version
0x00, 0x00, 0x00, 0x00, // compatibility_version
0x2f, 0x75, 0x73, 0x72, 0x00, 0x00, 0x00, 0x00, // data
};
var cmd = GenericCommandWithData(dylib_command){
.inner = .{
.cmd = LC_LOAD_DYLIB,
.cmdsize = 32,
.dylib = .{
.name = 24,
.timestamp = 2,
.current_version = 0,
.compatibility_version = 0,
},
},
};
cmd.data = try gpa.alloc(u8, 8);
defer gpa.free(cmd.data);
cmd.data[0] = 0x2f;
cmd.data[1] = 0x75;
cmd.data[2] = 0x73;
cmd.data[3] = 0x72;
cmd.data[4] = 0x0;
cmd.data[5] = 0x0;
cmd.data[6] = 0x0;
cmd.data[7] = 0x0;
try testRead(gpa, in_buffer, LoadCommand{ .dylib = cmd });
var out_buffer: [in_buffer.len]u8 = undefined;
try testWrite(&out_buffer, LoadCommand{ .dylib = cmd }, in_buffer);
}
test "read-write C struct command" {
// TODO compiling for macOS from big-endian arch
if (builtin.target.cpu.arch.endian() != .Little) return error.SkipZigTest;
var gpa = testing.allocator;
const in_buffer = &[_]u8{
0x28, 0x00, 0x00, 0x80, // cmd
0x18, 0x00, 0x00, 0x00, // cmdsize
0x04, 0x41, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // entryoff
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // stacksize
};
const cmd = .{
.cmd = LC_MAIN,
.cmdsize = 24,
.entryoff = 16644,
.stacksize = 0,
};
try testRead(gpa, in_buffer, LoadCommand{ .main = cmd });
var out_buffer: [in_buffer.len]u8 = undefined;
try testWrite(&out_buffer, LoadCommand{ .main = cmd }, in_buffer);
}

View File

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

View File

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

View File

@@ -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}", .{

View File

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

View File

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

View File

@@ -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(&sect));
}
}
pub fn deinit(self: *SegmentCommand, alloc: Allocator) void {
self.sections.deinit(alloc);
}
pub fn allocatedSize(self: SegmentCommand, start: u64) u64 {
assert(start >= self.inner.fileoff);
var min_pos: u64 = self.inner.fileoff + self.inner.filesize;
for (self.sections.items) |section| {
if (section.offset <= start) continue;
if (section.offset < min_pos) min_pos = section.offset;
}
return min_pos - start;
}
fn detectAllocCollision(self: SegmentCommand, start: u64, size: u64) ?u64 {
const end = start + padToIdeal(size);
for (self.sections.items) |section| {
const increased_size = padToIdeal(section.size);
const test_end = section.offset + increased_size;
if (end > section.offset and start < test_end) {
return test_end;
}
}
return null;
}
pub fn findFreeSpace(self: SegmentCommand, object_size: u64, min_alignment: u64, start: ?u64) u64 {
var offset: u64 = if (start) |v| v else self.inner.fileoff;
while (self.detectAllocCollision(offset, object_size)) |item_end| {
offset = mem.alignForwardGeneric(u64, item_end, min_alignment);
}
return offset;
}
fn eql(self: SegmentCommand, other: SegmentCommand) bool {
if (!meta.eql(self.inner, other.inner)) return false;
const lhs = self.sections.items;
const rhs = other.sections.items;
var i: usize = 0;
while (i < self.inner.nsects) : (i += 1) {
if (!meta.eql(lhs[i], rhs[i])) return false;
}
return true;
}
};
pub fn emptyGenericCommandWithData(cmd: anytype) GenericCommandWithData(@TypeOf(cmd)) {
return .{ .inner = cmd };
}
pub fn GenericCommandWithData(comptime Cmd: type) type {
return struct {
inner: Cmd,
/// This field remains undefined until `read` is called.
data: []u8 = undefined,
const Self = @This();
pub fn read(allocator: Allocator, reader: anytype) !Self {
const inner = try reader.readStruct(Cmd);
var data = try allocator.alloc(u8, inner.cmdsize - @sizeOf(Cmd));
errdefer allocator.free(data);
try reader.readNoEof(data);
return Self{
.inner = inner,
.data = data,
};
}
pub fn write(self: Self, writer: anytype) !void {
try writer.writeAll(mem.asBytes(&self.inner));
try writer.writeAll(self.data);
}
pub fn deinit(self: *Self, allocator: Allocator) void {
allocator.free(self.data);
}
fn eql(self: Self, other: Self) bool {
if (!meta.eql(self.inner, other.inner)) return false;
return mem.eql(u8, self.data, other.data);
}
};
}
pub fn createLoadDylibCommand(
allocator: Allocator,
name: []const u8,
timestamp: u32,
current_version: u32,
compatibility_version: u32,
) !GenericCommandWithData(macho.dylib_command) {
const cmdsize = @intCast(u32, mem.alignForwardGeneric(
u64,
@sizeOf(macho.dylib_command) + name.len + 1, // +1 for nul
@sizeOf(u64),
));
var dylib_cmd = emptyGenericCommandWithData(macho.dylib_command{
.cmd = macho.LC_LOAD_DYLIB,
.cmdsize = cmdsize,
.dylib = .{
.name = @sizeOf(macho.dylib_command),
.timestamp = timestamp,
.current_version = current_version,
.compatibility_version = compatibility_version,
},
});
dylib_cmd.data = try allocator.alloc(u8, cmdsize - dylib_cmd.inner.dylib.name);
mem.set(u8, dylib_cmd.data, 0);
mem.copy(u8, dylib_cmd.data, name);
return dylib_cmd;
}
fn 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(&sect.segname);
}
pub fn sectionName(sect: macho.section_64) []const u8 {
return parseName(&sect.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);
}