mirror of
https://github.com/ziglang/zig.git
synced 2026-01-06 21:43:25 +00:00
macho: sync with zld
gitrev a2c32e972f8c5adfcda8ed2d99379ae868f59c24
a2c32e972f
This commit is contained in:
parent
4c750016eb
commit
f26d5ee7ea
@ -283,7 +283,14 @@ fn make(step: *Step) !void {
|
||||
|
||||
const gpa = self.builder.allocator;
|
||||
const src_path = self.source.getPath(self.builder);
|
||||
const contents = try fs.cwd().readFileAlloc(gpa, src_path, self.max_bytes);
|
||||
const contents = try fs.cwd().readFileAllocOptions(
|
||||
gpa,
|
||||
src_path,
|
||||
self.max_bytes,
|
||||
null,
|
||||
@alignOf(u64),
|
||||
null,
|
||||
);
|
||||
|
||||
const output = switch (self.obj_format) {
|
||||
.macho => try MachODumper.parseAndDump(contents, .{
|
||||
@ -370,9 +377,10 @@ const Opts = struct {
|
||||
};
|
||||
|
||||
const MachODumper = struct {
|
||||
const LoadCommandIterator = macho.LoadCommandIterator;
|
||||
const symtab_label = "symtab";
|
||||
|
||||
fn parseAndDump(bytes: []const u8, opts: Opts) ![]const u8 {
|
||||
fn parseAndDump(bytes: []align(@alignOf(u64)) const u8, opts: Opts) ![]const u8 {
|
||||
const gpa = opts.gpa orelse unreachable; // MachO dumper requires an allocator
|
||||
var stream = std.io.fixedBufferStream(bytes);
|
||||
const reader = stream.reader();
|
||||
@ -385,55 +393,54 @@ const MachODumper = struct {
|
||||
var output = std.ArrayList(u8).init(gpa);
|
||||
const writer = output.writer();
|
||||
|
||||
var load_commands = std.ArrayList(macho.LoadCommand).init(gpa);
|
||||
try load_commands.ensureTotalCapacity(hdr.ncmds);
|
||||
|
||||
var sections = std.ArrayList(struct { seg: u16, sect: u16 }).init(gpa);
|
||||
var imports = std.ArrayList(u16).init(gpa);
|
||||
|
||||
var symtab_cmd: ?u16 = null;
|
||||
var i: u16 = 0;
|
||||
while (i < hdr.ncmds) : (i += 1) {
|
||||
var cmd = try macho.LoadCommand.read(gpa, reader);
|
||||
load_commands.appendAssumeCapacity(cmd);
|
||||
var symtab: []const macho.nlist_64 = undefined;
|
||||
var strtab: []const u8 = undefined;
|
||||
var sections = std.ArrayList(macho.section_64).init(gpa);
|
||||
var imports = std.ArrayList([]const u8).init(gpa);
|
||||
|
||||
var it = LoadCommandIterator{
|
||||
.ncmds = hdr.ncmds,
|
||||
.buffer = bytes[@sizeOf(macho.mach_header_64)..][0..hdr.sizeofcmds],
|
||||
};
|
||||
var i: usize = 0;
|
||||
while (it.next()) |cmd| {
|
||||
switch (cmd.cmd()) {
|
||||
.SEGMENT_64 => {
|
||||
const seg = cmd.segment;
|
||||
for (seg.sections.items) |_, j| {
|
||||
try sections.append(.{ .seg = i, .sect = @intCast(u16, j) });
|
||||
const seg = cmd.cast(macho.segment_command_64).?;
|
||||
try sections.ensureUnusedCapacity(seg.nsects);
|
||||
for (cmd.getSections()) |sect| {
|
||||
sections.appendAssumeCapacity(sect);
|
||||
}
|
||||
},
|
||||
.SYMTAB => {
|
||||
symtab_cmd = i;
|
||||
.SYMTAB => if (opts.dump_symtab) {
|
||||
const lc = cmd.cast(macho.symtab_command).?;
|
||||
symtab = @ptrCast(
|
||||
[*]const macho.nlist_64,
|
||||
@alignCast(@alignOf(macho.nlist_64), &bytes[lc.symoff]),
|
||||
)[0..lc.nsyms];
|
||||
strtab = bytes[lc.stroff..][0..lc.strsize];
|
||||
},
|
||||
.LOAD_DYLIB,
|
||||
.LOAD_WEAK_DYLIB,
|
||||
.REEXPORT_DYLIB,
|
||||
=> {
|
||||
try imports.append(i);
|
||||
try imports.append(cmd.getDylibPathName());
|
||||
},
|
||||
else => {},
|
||||
}
|
||||
|
||||
try dumpLoadCommand(cmd, i, writer);
|
||||
try writer.writeByte('\n');
|
||||
|
||||
i += 1;
|
||||
}
|
||||
|
||||
if (opts.dump_symtab) {
|
||||
const cmd = load_commands.items[symtab_cmd.?].symtab;
|
||||
try writer.writeAll(symtab_label ++ "\n");
|
||||
const strtab = bytes[cmd.stroff..][0..cmd.strsize];
|
||||
const raw_symtab = bytes[cmd.symoff..][0 .. cmd.nsyms * @sizeOf(macho.nlist_64)];
|
||||
const symtab = mem.bytesAsSlice(macho.nlist_64, raw_symtab);
|
||||
|
||||
for (symtab) |sym| {
|
||||
if (sym.stab()) continue;
|
||||
const sym_name = mem.sliceTo(@ptrCast([*:0]const u8, strtab.ptr + sym.n_strx), 0);
|
||||
if (sym.sect()) {
|
||||
const map = sections.items[sym.n_sect - 1];
|
||||
const seg = load_commands.items[map.seg].segment;
|
||||
const sect = seg.sections.items[map.sect];
|
||||
const sect = sections.items[sym.n_sect - 1];
|
||||
try writer.print("{x} ({s},{s})", .{
|
||||
sym.n_value,
|
||||
sect.segName(),
|
||||
@ -455,9 +462,7 @@ const MachODumper = struct {
|
||||
break :blk "flat lookup";
|
||||
unreachable;
|
||||
}
|
||||
const import_id = imports.items[@bitCast(u16, ordinal) - 1];
|
||||
const import = load_commands.items[import_id].dylib;
|
||||
const full_path = mem.sliceTo(import.data, 0);
|
||||
const full_path = imports.items[@bitCast(u16, ordinal) - 1];
|
||||
const basename = fs.path.basename(full_path);
|
||||
assert(basename.len > 0);
|
||||
const ext = mem.lastIndexOfScalar(u8, basename, '.') orelse basename.len;
|
||||
@ -481,7 +486,7 @@ const MachODumper = struct {
|
||||
return output.toOwnedSlice();
|
||||
}
|
||||
|
||||
fn dumpLoadCommand(lc: macho.LoadCommand, index: u16, writer: anytype) !void {
|
||||
fn dumpLoadCommand(lc: macho.LoadCommandIterator.LoadCommand, index: usize, writer: anytype) !void {
|
||||
// print header first
|
||||
try writer.print(
|
||||
\\LC {d}
|
||||
@ -491,8 +496,7 @@ const MachODumper = struct {
|
||||
|
||||
switch (lc.cmd()) {
|
||||
.SEGMENT_64 => {
|
||||
// TODO dump section headers
|
||||
const seg = lc.segment.inner;
|
||||
const seg = lc.cast(macho.segment_command_64).?;
|
||||
try writer.writeByte('\n');
|
||||
try writer.print(
|
||||
\\segname {s}
|
||||
@ -508,7 +512,7 @@ const MachODumper = struct {
|
||||
seg.filesize,
|
||||
});
|
||||
|
||||
for (lc.segment.sections.items) |sect| {
|
||||
for (lc.getSections()) |sect| {
|
||||
try writer.writeByte('\n');
|
||||
try writer.print(
|
||||
\\sectname {s}
|
||||
@ -531,7 +535,7 @@ const MachODumper = struct {
|
||||
.LOAD_WEAK_DYLIB,
|
||||
.REEXPORT_DYLIB,
|
||||
=> {
|
||||
const dylib = lc.dylib.inner.dylib;
|
||||
const dylib = lc.cast(macho.dylib_command).?;
|
||||
try writer.writeByte('\n');
|
||||
try writer.print(
|
||||
\\name {s}
|
||||
@ -539,19 +543,20 @@ const MachODumper = struct {
|
||||
\\current version {x}
|
||||
\\compatibility version {x}
|
||||
, .{
|
||||
mem.sliceTo(lc.dylib.data, 0),
|
||||
dylib.timestamp,
|
||||
dylib.current_version,
|
||||
dylib.compatibility_version,
|
||||
lc.getDylibPathName(),
|
||||
dylib.dylib.timestamp,
|
||||
dylib.dylib.current_version,
|
||||
dylib.dylib.compatibility_version,
|
||||
});
|
||||
},
|
||||
|
||||
.MAIN => {
|
||||
const main = lc.cast(macho.entry_point_command).?;
|
||||
try writer.writeByte('\n');
|
||||
try writer.print(
|
||||
\\entryoff {x}
|
||||
\\stacksize {x}
|
||||
, .{ lc.main.entryoff, lc.main.stacksize });
|
||||
, .{ main.entryoff, main.stacksize });
|
||||
},
|
||||
|
||||
.RPATH => {
|
||||
@ -559,7 +564,7 @@ const MachODumper = struct {
|
||||
try writer.print(
|
||||
\\path {s}
|
||||
, .{
|
||||
mem.sliceTo(lc.rpath.data, 0),
|
||||
lc.getRpathPathName(),
|
||||
});
|
||||
},
|
||||
|
||||
|
||||
@ -1835,429 +1835,70 @@ pub const data_in_code_entry = extern struct {
|
||||
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 const LoadCommandIterator = struct {
|
||||
ncmds: usize,
|
||||
buffer: []align(@alignOf(u64)) const u8,
|
||||
index: usize = 0,
|
||||
|
||||
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);
|
||||
pub const LoadCommand = struct {
|
||||
hdr: load_command,
|
||||
data: []const u8,
|
||||
|
||||
return switch (header.cmd) {
|
||||
.SEGMENT_64 => LoadCommand{
|
||||
.segment = try SegmentCommand.read(allocator, stream.reader()),
|
||||
},
|
||||
.DYLD_INFO, .DYLD_INFO_ONLY => LoadCommand{
|
||||
.dyld_info_only = try stream.reader().readStruct(dyld_info_command),
|
||||
},
|
||||
.SYMTAB => LoadCommand{
|
||||
.symtab = try stream.reader().readStruct(symtab_command),
|
||||
},
|
||||
.DYSYMTAB => LoadCommand{
|
||||
.dysymtab = try stream.reader().readStruct(dysymtab_command),
|
||||
},
|
||||
.ID_DYLINKER, .LOAD_DYLINKER, .DYLD_ENVIRONMENT => LoadCommand{
|
||||
.dylinker = try GenericCommandWithData(dylinker_command).read(allocator, stream.reader()),
|
||||
},
|
||||
.ID_DYLIB, .LOAD_WEAK_DYLIB, .LOAD_DYLIB, .REEXPORT_DYLIB => LoadCommand{
|
||||
.dylib = try GenericCommandWithData(dylib_command).read(allocator, stream.reader()),
|
||||
},
|
||||
.MAIN => LoadCommand{
|
||||
.main = try stream.reader().readStruct(entry_point_command),
|
||||
},
|
||||
.VERSION_MIN_MACOSX, .VERSION_MIN_IPHONEOS, .VERSION_MIN_WATCHOS, .VERSION_MIN_TVOS => LoadCommand{
|
||||
.version_min = try stream.reader().readStruct(version_min_command),
|
||||
},
|
||||
.SOURCE_VERSION => LoadCommand{
|
||||
.source_version = try stream.reader().readStruct(source_version_command),
|
||||
},
|
||||
.BUILD_VERSION => LoadCommand{
|
||||
.build_version = try GenericCommandWithData(build_version_command).read(allocator, stream.reader()),
|
||||
},
|
||||
.UUID => LoadCommand{
|
||||
.uuid = try stream.reader().readStruct(uuid_command),
|
||||
},
|
||||
.FUNCTION_STARTS, .DATA_IN_CODE, .CODE_SIGNATURE => LoadCommand{
|
||||
.linkedit_data = try stream.reader().readStruct(linkedit_data_command),
|
||||
},
|
||||
.RPATH => LoadCommand{
|
||||
.rpath = try GenericCommandWithData(rpath_command).read(allocator, stream.reader()),
|
||||
},
|
||||
else => LoadCommand{
|
||||
.unknown = try GenericCommandWithData(load_command).read(allocator, stream.reader()),
|
||||
},
|
||||
pub fn cmd(lc: LoadCommand) LC {
|
||||
return lc.hdr.cmd;
|
||||
}
|
||||
|
||||
pub fn cmdsize(lc: LoadCommand) u32 {
|
||||
return lc.hdr.cmdsize;
|
||||
}
|
||||
|
||||
pub fn cast(lc: LoadCommand, comptime Cmd: type) ?Cmd {
|
||||
if (lc.data.len < @sizeOf(Cmd)) return null;
|
||||
return @ptrCast(*const Cmd, @alignCast(@alignOf(Cmd), &lc.data[0])).*;
|
||||
}
|
||||
|
||||
/// Asserts LoadCommand is of type segment_command_64.
|
||||
pub fn getSections(lc: LoadCommand) []const section_64 {
|
||||
const segment_lc = lc.cast(segment_command_64).?;
|
||||
if (segment_lc.nsects == 0) return &[0]section_64{};
|
||||
const data = lc.data[@sizeOf(segment_command_64)..];
|
||||
const sections = @ptrCast(
|
||||
[*]const section_64,
|
||||
@alignCast(@alignOf(section_64), &data[0]),
|
||||
)[0..segment_lc.nsects];
|
||||
return sections;
|
||||
}
|
||||
|
||||
/// Asserts LoadCommand is of type dylib_command.
|
||||
pub fn getDylibPathName(lc: LoadCommand) []const u8 {
|
||||
const dylib_lc = lc.cast(dylib_command).?;
|
||||
const data = lc.data[dylib_lc.dylib.name..];
|
||||
return mem.sliceTo(data, 0);
|
||||
}
|
||||
|
||||
/// Asserts LoadCommand is of type rpath_command.
|
||||
pub fn getRpathPathName(lc: LoadCommand) []const u8 {
|
||||
const rpath_lc = lc.cast(rpath_command).?;
|
||||
const data = lc.data[rpath_lc.path..];
|
||||
return mem.sliceTo(data, 0);
|
||||
}
|
||||
};
|
||||
|
||||
pub fn next(it: *LoadCommandIterator) ?LoadCommand {
|
||||
if (it.index >= it.ncmds) return null;
|
||||
|
||||
const hdr = @ptrCast(
|
||||
*const load_command,
|
||||
@alignCast(@alignOf(load_command), &it.buffer[0]),
|
||||
).*;
|
||||
const cmd = LoadCommand{
|
||||
.hdr = hdr,
|
||||
.data = it.buffer[0..hdr.cmdsize],
|
||||
};
|
||||
}
|
||||
|
||||
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),
|
||||
};
|
||||
}
|
||||
it.buffer = it.buffer[hdr.cmdsize..];
|
||||
it.index += 1;
|
||||
|
||||
pub fn cmd(self: LoadCommand) LC {
|
||||
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),
|
||||
};
|
||||
return cmd;
|
||||
}
|
||||
};
|
||||
|
||||
/// A Zig wrapper for segment_command_64.
|
||||
/// Encloses the extern struct together with a list of sections for this segment.
|
||||
pub const SegmentCommand = struct {
|
||||
inner: segment_command_64,
|
||||
sections: std.ArrayListUnmanaged(section_64) = .{},
|
||||
|
||||
pub fn read(allocator: Allocator, reader: anytype) !SegmentCommand {
|
||||
const inner = try reader.readStruct(segment_command_64);
|
||||
var segment = SegmentCommand{
|
||||
.inner = inner,
|
||||
};
|
||||
try segment.sections.ensureTotalCapacityPrecise(allocator, inner.nsects);
|
||||
|
||||
var i: usize = 0;
|
||||
while (i < inner.nsects) : (i += 1) {
|
||||
const sect = try reader.readStruct(section_64);
|
||||
segment.sections.appendAssumeCapacity(sect);
|
||||
}
|
||||
|
||||
return segment;
|
||||
}
|
||||
|
||||
pub fn write(self: SegmentCommand, writer: anytype) !void {
|
||||
try writer.writeAll(mem.asBytes(&self.inner));
|
||||
for (self.sections.items) |sect| {
|
||||
try writer.writeAll(mem.asBytes(§));
|
||||
}
|
||||
}
|
||||
|
||||
pub fn deinit(self: *SegmentCommand, allocator: Allocator) void {
|
||||
self.sections.deinit(allocator);
|
||||
}
|
||||
|
||||
pub fn eql(self: SegmentCommand, other: SegmentCommand) bool {
|
||||
if (!meta.eql(self.inner, other.inner)) return false;
|
||||
const lhs = self.sections.items;
|
||||
const rhs = other.sections.items;
|
||||
var i: usize = 0;
|
||||
while (i < self.inner.nsects) : (i += 1) {
|
||||
if (!meta.eql(lhs[i], rhs[i])) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
pub fn emptyGenericCommandWithData(cmd: anytype) GenericCommandWithData(@TypeOf(cmd)) {
|
||||
return .{ .inner = cmd };
|
||||
}
|
||||
|
||||
/// A Zig wrapper for a generic load command with variable-length data.
|
||||
pub fn GenericCommandWithData(comptime Cmd: type) type {
|
||||
return struct {
|
||||
inner: Cmd,
|
||||
/// This field remains undefined until `read` is called.
|
||||
data: []u8 = undefined,
|
||||
|
||||
const Self = @This();
|
||||
|
||||
pub fn read(allocator: Allocator, reader: anytype) !Self {
|
||||
const inner = try reader.readStruct(Cmd);
|
||||
var data = try allocator.alloc(u8, inner.cmdsize - @sizeOf(Cmd));
|
||||
errdefer allocator.free(data);
|
||||
try reader.readNoEof(data);
|
||||
return Self{
|
||||
.inner = inner,
|
||||
.data = data,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn write(self: Self, writer: anytype) !void {
|
||||
try writer.writeAll(mem.asBytes(&self.inner));
|
||||
try writer.writeAll(self.data);
|
||||
}
|
||||
|
||||
pub fn deinit(self: *Self, allocator: Allocator) void {
|
||||
allocator.free(self.data);
|
||||
}
|
||||
|
||||
pub fn eql(self: Self, other: Self) bool {
|
||||
if (!meta.eql(self.inner, other.inner)) return false;
|
||||
return mem.eql(u8, self.data, other.data);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
pub fn createLoadDylibCommand(
|
||||
allocator: Allocator,
|
||||
cmd_id: LC,
|
||||
name: []const u8,
|
||||
timestamp: u32,
|
||||
current_version: u32,
|
||||
compatibility_version: u32,
|
||||
) !GenericCommandWithData(dylib_command) {
|
||||
assert(cmd_id == .LOAD_DYLIB or cmd_id == .LOAD_WEAK_DYLIB or cmd_id == .REEXPORT_DYLIB or cmd_id == .ID_DYLIB);
|
||||
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 = cmd_id,
|
||||
.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 = PROT.READ | PROT.WRITE | PROT.EXEC,
|
||||
.initprot = PROT.EXEC | 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 = .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 = .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);
|
||||
}
|
||||
|
||||
@ -853,8 +853,7 @@ pub fn commitDeclState(
|
||||
.macho => {
|
||||
const macho_file = file.cast(File.MachO).?;
|
||||
const d_sym = &macho_file.d_sym.?;
|
||||
const dwarf_segment = &d_sym.load_commands.items[d_sym.dwarf_segment_cmd_index.?].segment;
|
||||
const debug_line_sect = &dwarf_segment.sections.items[d_sym.debug_line_section_index.?];
|
||||
const debug_line_sect = &d_sym.sections.items[d_sym.debug_line_section_index.?];
|
||||
const file_pos = debug_line_sect.offset + src_fn.off;
|
||||
try pwriteDbgLineNops(d_sym.file, file_pos, 0, &[0]u8{}, src_fn.len);
|
||||
},
|
||||
@ -933,8 +932,8 @@ pub fn commitDeclState(
|
||||
.macho => {
|
||||
const macho_file = file.cast(File.MachO).?;
|
||||
const d_sym = &macho_file.d_sym.?;
|
||||
const dwarf_segment = &d_sym.load_commands.items[d_sym.dwarf_segment_cmd_index.?].segment;
|
||||
const debug_line_sect = &dwarf_segment.sections.items[d_sym.debug_line_section_index.?];
|
||||
const dwarf_segment = d_sym.segments.items[d_sym.dwarf_segment_cmd_index.?];
|
||||
const debug_line_sect = &d_sym.sections.items[d_sym.debug_line_section_index.?];
|
||||
if (needed_size != debug_line_sect.size) {
|
||||
if (needed_size > d_sym.allocatedSize(debug_line_sect.offset)) {
|
||||
const new_offset = d_sym.findFreeSpace(needed_size, 1);
|
||||
@ -955,10 +954,9 @@ pub fn commitDeclState(
|
||||
);
|
||||
|
||||
debug_line_sect.offset = @intCast(u32, new_offset);
|
||||
debug_line_sect.addr = dwarf_segment.inner.vmaddr + new_offset - dwarf_segment.inner.fileoff;
|
||||
debug_line_sect.addr = dwarf_segment.vmaddr + new_offset - dwarf_segment.fileoff;
|
||||
}
|
||||
debug_line_sect.size = needed_size;
|
||||
d_sym.load_commands_dirty = true; // TODO look into making only the one section dirty
|
||||
d_sym.debug_line_header_dirty = true;
|
||||
}
|
||||
const file_pos = debug_line_sect.offset + src_fn.off;
|
||||
@ -1137,8 +1135,7 @@ fn updateDeclDebugInfoAllocation(self: *Dwarf, file: *File, atom: *Atom, len: u3
|
||||
.macho => {
|
||||
const macho_file = file.cast(File.MachO).?;
|
||||
const d_sym = &macho_file.d_sym.?;
|
||||
const dwarf_segment = &d_sym.load_commands.items[d_sym.dwarf_segment_cmd_index.?].segment;
|
||||
const debug_info_sect = &dwarf_segment.sections.items[d_sym.debug_info_section_index.?];
|
||||
const debug_info_sect = &d_sym.sections.items[d_sym.debug_info_section_index.?];
|
||||
const file_pos = debug_info_sect.offset + atom.off;
|
||||
try pwriteDbgInfoNops(d_sym.file, file_pos, 0, &[0]u8{}, atom.len, false);
|
||||
},
|
||||
@ -1235,8 +1232,8 @@ fn writeDeclDebugInfo(self: *Dwarf, file: *File, atom: *Atom, dbg_info_buf: []co
|
||||
.macho => {
|
||||
const macho_file = file.cast(File.MachO).?;
|
||||
const d_sym = &macho_file.d_sym.?;
|
||||
const dwarf_segment = &d_sym.load_commands.items[d_sym.dwarf_segment_cmd_index.?].segment;
|
||||
const debug_info_sect = &dwarf_segment.sections.items[d_sym.debug_info_section_index.?];
|
||||
const dwarf_segment = d_sym.segments.items[d_sym.dwarf_segment_cmd_index.?];
|
||||
const debug_info_sect = &d_sym.sections.items[d_sym.debug_info_section_index.?];
|
||||
if (needed_size != debug_info_sect.size) {
|
||||
if (needed_size > d_sym.allocatedSize(debug_info_sect.offset)) {
|
||||
const new_offset = d_sym.findFreeSpace(needed_size, 1);
|
||||
@ -1257,10 +1254,9 @@ fn writeDeclDebugInfo(self: *Dwarf, file: *File, atom: *Atom, dbg_info_buf: []co
|
||||
);
|
||||
|
||||
debug_info_sect.offset = @intCast(u32, new_offset);
|
||||
debug_info_sect.addr = dwarf_segment.inner.vmaddr + new_offset - dwarf_segment.inner.fileoff;
|
||||
debug_info_sect.addr = dwarf_segment.vmaddr + new_offset - dwarf_segment.fileoff;
|
||||
}
|
||||
debug_info_sect.size = needed_size;
|
||||
d_sym.load_commands_dirty = true; // TODO look into making only the one section dirty
|
||||
d_sym.debug_line_header_dirty = true;
|
||||
}
|
||||
const file_pos = debug_info_sect.offset + atom.off;
|
||||
@ -1330,8 +1326,7 @@ pub fn updateDeclLineNumber(self: *Dwarf, file: *File, decl: *const Module.Decl)
|
||||
.macho => {
|
||||
const macho_file = file.cast(File.MachO).?;
|
||||
const d_sym = macho_file.d_sym.?;
|
||||
const dwarf_seg = d_sym.load_commands.items[d_sym.dwarf_segment_cmd_index.?].segment;
|
||||
const sect = dwarf_seg.sections.items[d_sym.debug_line_section_index.?];
|
||||
const sect = d_sym.sections.items[d_sym.debug_line_section_index.?];
|
||||
const file_pos = sect.offset + decl.fn_link.macho.off + self.getRelocDbgLineOff();
|
||||
try d_sym.file.pwriteAll(&data, file_pos);
|
||||
},
|
||||
@ -1557,14 +1552,14 @@ pub fn writeDbgAbbrev(self: *Dwarf, file: *File) !void {
|
||||
.macho => {
|
||||
const macho_file = file.cast(File.MachO).?;
|
||||
const d_sym = &macho_file.d_sym.?;
|
||||
const dwarf_segment = &d_sym.load_commands.items[d_sym.dwarf_segment_cmd_index.?].segment;
|
||||
const debug_abbrev_sect = &dwarf_segment.sections.items[d_sym.debug_abbrev_section_index.?];
|
||||
const dwarf_segment = d_sym.segments.items[d_sym.dwarf_segment_cmd_index.?];
|
||||
const debug_abbrev_sect = &d_sym.sections.items[d_sym.debug_abbrev_section_index.?];
|
||||
const allocated_size = d_sym.allocatedSize(debug_abbrev_sect.offset);
|
||||
if (needed_size > allocated_size) {
|
||||
debug_abbrev_sect.size = 0; // free the space
|
||||
const offset = d_sym.findFreeSpace(needed_size, 1);
|
||||
debug_abbrev_sect.offset = @intCast(u32, offset);
|
||||
debug_abbrev_sect.addr = dwarf_segment.inner.vmaddr + offset - dwarf_segment.inner.fileoff;
|
||||
debug_abbrev_sect.addr = dwarf_segment.vmaddr + offset - dwarf_segment.fileoff;
|
||||
}
|
||||
debug_abbrev_sect.size = needed_size;
|
||||
log.debug("__debug_abbrev start=0x{x} end=0x{x}", .{
|
||||
@ -1681,8 +1676,7 @@ pub fn writeDbgInfoHeader(self: *Dwarf, file: *File, module: *Module, low_pc: u6
|
||||
.macho => {
|
||||
const macho_file = file.cast(File.MachO).?;
|
||||
const d_sym = &macho_file.d_sym.?;
|
||||
const dwarf_seg = d_sym.load_commands.items[d_sym.dwarf_segment_cmd_index.?].segment;
|
||||
const debug_info_sect = dwarf_seg.sections.items[d_sym.debug_info_section_index.?];
|
||||
const debug_info_sect = d_sym.sections.items[d_sym.debug_info_section_index.?];
|
||||
const file_pos = debug_info_sect.offset;
|
||||
try pwriteDbgInfoNops(d_sym.file, file_pos, 0, di_buf.items, jmp_amt, false);
|
||||
},
|
||||
@ -1998,13 +1992,13 @@ pub fn writeDbgAranges(self: *Dwarf, file: *File, addr: u64, size: u64) !void {
|
||||
.macho => {
|
||||
const macho_file = file.cast(File.MachO).?;
|
||||
const d_sym = &macho_file.d_sym.?;
|
||||
const dwarf_seg = &d_sym.load_commands.items[d_sym.dwarf_segment_cmd_index.?].segment;
|
||||
const debug_aranges_sect = &dwarf_seg.sections.items[d_sym.debug_aranges_section_index.?];
|
||||
const dwarf_seg = d_sym.segments.items[d_sym.dwarf_segment_cmd_index.?];
|
||||
const debug_aranges_sect = &d_sym.sections.items[d_sym.debug_aranges_section_index.?];
|
||||
const allocated_size = d_sym.allocatedSize(debug_aranges_sect.offset);
|
||||
if (needed_size > allocated_size) {
|
||||
debug_aranges_sect.size = 0; // free the space
|
||||
const new_offset = d_sym.findFreeSpace(needed_size, 16);
|
||||
debug_aranges_sect.addr = dwarf_seg.inner.vmaddr + new_offset - dwarf_seg.inner.fileoff;
|
||||
debug_aranges_sect.addr = dwarf_seg.vmaddr + new_offset - dwarf_seg.fileoff;
|
||||
debug_aranges_sect.offset = @intCast(u32, new_offset);
|
||||
}
|
||||
debug_aranges_sect.size = needed_size;
|
||||
@ -2134,8 +2128,7 @@ pub fn writeDbgLineHeader(self: *Dwarf, file: *File, module: *Module) !void {
|
||||
.macho => {
|
||||
const macho_file = file.cast(File.MachO).?;
|
||||
const d_sym = &macho_file.d_sym.?;
|
||||
const dwarf_seg = d_sym.load_commands.items[d_sym.dwarf_segment_cmd_index.?].segment;
|
||||
const debug_line_sect = dwarf_seg.sections.items[d_sym.debug_line_section_index.?];
|
||||
const debug_line_sect = d_sym.sections.items[d_sym.debug_line_section_index.?];
|
||||
const file_pos = debug_line_sect.offset;
|
||||
try pwriteDbgLineNops(d_sym.file, file_pos, 0, di_buf.items, jmp_amt);
|
||||
},
|
||||
@ -2264,8 +2257,7 @@ pub fn flushModule(self: *Dwarf, file: *File, module: *Module) !void {
|
||||
.macho => {
|
||||
const macho_file = file.cast(File.MachO).?;
|
||||
const d_sym = &macho_file.d_sym.?;
|
||||
const dwarf_segment = &d_sym.load_commands.items[d_sym.dwarf_segment_cmd_index.?].segment;
|
||||
const debug_info_sect = &dwarf_segment.sections.items[d_sym.debug_info_section_index.?];
|
||||
const debug_info_sect = &d_sym.sections.items[d_sym.debug_info_section_index.?];
|
||||
break :blk debug_info_sect.offset;
|
||||
},
|
||||
// for wasm, the offset is always 0 as we write to memory first
|
||||
|
||||
3463
src/link/MachO.zig
3463
src/link/MachO.zig
File diff suppressed because it is too large
Load Diff
@ -6,19 +6,14 @@ const fs = std.fs;
|
||||
const log = std.log.scoped(.link);
|
||||
const macho = std.macho;
|
||||
const mem = std.mem;
|
||||
const fat = @import("fat.zig");
|
||||
|
||||
const Allocator = mem.Allocator;
|
||||
const Object = @import("Object.zig");
|
||||
|
||||
file: fs.File,
|
||||
fat_offset: u64,
|
||||
name: []const u8,
|
||||
|
||||
header: ?ar_hdr = null,
|
||||
|
||||
// The actual contents we care about linking with will be embedded at
|
||||
// an offset within a file if we are linking against a fat lib
|
||||
library_offset: u64 = 0,
|
||||
header: ar_hdr = undefined,
|
||||
|
||||
/// Parsed table of contents.
|
||||
/// Each symbol name points to a list of all definition
|
||||
@ -103,11 +98,7 @@ pub fn deinit(self: *Archive, allocator: Allocator) void {
|
||||
allocator.free(self.name);
|
||||
}
|
||||
|
||||
pub fn parse(self: *Archive, allocator: Allocator, cpu_arch: std.Target.Cpu.Arch) !void {
|
||||
const reader = self.file.reader();
|
||||
self.library_offset = try fat.getLibraryOffset(reader, cpu_arch);
|
||||
try self.file.seekTo(self.library_offset);
|
||||
|
||||
pub fn parse(self: *Archive, allocator: Allocator, reader: anytype) !void {
|
||||
const magic = try reader.readBytesNoEof(SARMAG);
|
||||
if (!mem.eql(u8, &magic, ARMAG)) {
|
||||
log.debug("invalid magic: expected '{s}', found '{s}'", .{ ARMAG, magic });
|
||||
@ -115,21 +106,23 @@ pub fn parse(self: *Archive, allocator: Allocator, cpu_arch: std.Target.Cpu.Arch
|
||||
}
|
||||
|
||||
self.header = try reader.readStruct(ar_hdr);
|
||||
if (!mem.eql(u8, &self.header.?.ar_fmag, ARFMAG)) {
|
||||
log.debug("invalid header delimiter: expected '{s}', found '{s}'", .{ ARFMAG, self.header.?.ar_fmag });
|
||||
if (!mem.eql(u8, &self.header.ar_fmag, ARFMAG)) {
|
||||
log.debug("invalid header delimiter: expected '{s}', found '{s}'", .{
|
||||
ARFMAG,
|
||||
self.header.ar_fmag,
|
||||
});
|
||||
return error.NotArchive;
|
||||
}
|
||||
|
||||
var embedded_name = try parseName(allocator, self.header.?, reader);
|
||||
const name_or_length = try self.header.nameOrLength();
|
||||
var embedded_name = try parseName(allocator, name_or_length, reader);
|
||||
log.debug("parsing archive '{s}' at '{s}'", .{ embedded_name, self.name });
|
||||
defer allocator.free(embedded_name);
|
||||
|
||||
try self.parseTableOfContents(allocator, reader);
|
||||
try reader.context.seekTo(0);
|
||||
}
|
||||
|
||||
fn parseName(allocator: Allocator, header: ar_hdr, reader: anytype) ![]u8 {
|
||||
const name_or_length = try header.nameOrLength();
|
||||
fn parseName(allocator: Allocator, name_or_length: ar_hdr.NameOrLength, reader: anytype) ![]u8 {
|
||||
var name: []u8 = undefined;
|
||||
switch (name_or_length) {
|
||||
.Name => |n| {
|
||||
@ -187,9 +180,14 @@ fn parseTableOfContents(self: *Archive, allocator: Allocator, reader: anytype) !
|
||||
}
|
||||
}
|
||||
|
||||
pub fn parseObject(self: Archive, allocator: Allocator, cpu_arch: std.Target.Cpu.Arch, offset: u32) !Object {
|
||||
pub fn parseObject(
|
||||
self: Archive,
|
||||
allocator: Allocator,
|
||||
cpu_arch: std.Target.Cpu.Arch,
|
||||
offset: u32,
|
||||
) !Object {
|
||||
const reader = self.file.reader();
|
||||
try reader.context.seekTo(offset + self.library_offset);
|
||||
try reader.context.seekTo(self.fat_offset + offset);
|
||||
|
||||
const object_header = try reader.readStruct(ar_hdr);
|
||||
|
||||
@ -198,7 +196,8 @@ pub fn parseObject(self: Archive, allocator: Allocator, cpu_arch: std.Target.Cpu
|
||||
return error.MalformedArchive;
|
||||
}
|
||||
|
||||
const object_name = try parseName(allocator, object_header, reader);
|
||||
const name_or_length = try object_header.nameOrLength();
|
||||
const object_name = try parseName(allocator, name_or_length, reader);
|
||||
defer allocator.free(object_name);
|
||||
|
||||
log.debug("extracting object '{s}' from archive '{s}'", .{ object_name, self.name });
|
||||
@ -209,15 +208,24 @@ pub fn parseObject(self: Archive, allocator: Allocator, cpu_arch: std.Target.Cpu
|
||||
break :name try std.fmt.allocPrint(allocator, "{s}({s})", .{ path, object_name });
|
||||
};
|
||||
|
||||
const object_name_len = switch (name_or_length) {
|
||||
.Name => 0,
|
||||
.Length => |len| len,
|
||||
};
|
||||
const object_size = (try object_header.size()) - object_name_len;
|
||||
const contents = try allocator.allocWithOptions(u8, object_size, @alignOf(u64), null);
|
||||
const amt = try reader.readAll(contents);
|
||||
if (amt != object_size) {
|
||||
return error.InputOutput;
|
||||
}
|
||||
|
||||
var object = Object{
|
||||
.file = try fs.cwd().openFile(self.name, .{}),
|
||||
.name = name,
|
||||
.file_offset = @intCast(u32, try reader.context.getPos()),
|
||||
.mtime = try self.header.?.date(),
|
||||
.mtime = try self.header.date(),
|
||||
.contents = contents,
|
||||
};
|
||||
|
||||
try object.parse(allocator, cpu_arch);
|
||||
try reader.context.seekTo(0);
|
||||
|
||||
return object;
|
||||
}
|
||||
|
||||
@ -246,7 +246,7 @@ pub fn parseRelocs(self: *Atom, relocs: []const macho.relocation_info, context:
|
||||
else => {
|
||||
log.err("unexpected relocation type after ARM64_RELOC_ADDEND", .{});
|
||||
log.err(" expected ARM64_RELOC_PAGE21 or ARM64_RELOC_PAGEOFF12", .{});
|
||||
log.err(" found {}", .{next});
|
||||
log.err(" found {s}", .{@tagName(next)});
|
||||
return error.UnexpectedRelocationType;
|
||||
},
|
||||
}
|
||||
@ -285,7 +285,9 @@ pub fn parseRelocs(self: *Atom, relocs: []const macho.relocation_info, context:
|
||||
else => {
|
||||
log.err("unexpected relocation type after ARM64_RELOC_ADDEND", .{});
|
||||
log.err(" expected ARM64_RELOC_UNSIGNED", .{});
|
||||
log.err(" found {}", .{@intToEnum(macho.reloc_type_arm64, relocs[i + 1].r_type)});
|
||||
log.err(" found {s}", .{
|
||||
@tagName(@intToEnum(macho.reloc_type_arm64, relocs[i + 1].r_type)),
|
||||
});
|
||||
return error.UnexpectedRelocationType;
|
||||
},
|
||||
},
|
||||
@ -294,7 +296,9 @@ pub fn parseRelocs(self: *Atom, relocs: []const macho.relocation_info, context:
|
||||
else => {
|
||||
log.err("unexpected relocation type after X86_64_RELOC_ADDEND", .{});
|
||||
log.err(" expected X86_64_RELOC_UNSIGNED", .{});
|
||||
log.err(" found {}", .{@intToEnum(macho.reloc_type_x86_64, relocs[i + 1].r_type)});
|
||||
log.err(" found {s}", .{
|
||||
@tagName(@intToEnum(macho.reloc_type_x86_64, relocs[i + 1].r_type)),
|
||||
});
|
||||
return error.UnexpectedRelocationType;
|
||||
},
|
||||
},
|
||||
@ -309,13 +313,13 @@ pub fn parseRelocs(self: *Atom, relocs: []const macho.relocation_info, context:
|
||||
const sect_id = @intCast(u16, rel.r_symbolnum - 1);
|
||||
const sym_index = object.sections_as_symbols.get(sect_id) orelse blk: {
|
||||
const sect = object.getSourceSection(sect_id);
|
||||
const match = (try context.macho_file.getMatchingSection(sect)) orelse
|
||||
const match = (try context.macho_file.getOutputSection(sect)) orelse
|
||||
unreachable;
|
||||
const sym_index = @intCast(u32, object.symtab.items.len);
|
||||
try object.symtab.append(gpa, .{
|
||||
.n_strx = 0,
|
||||
.n_type = macho.N_SECT,
|
||||
.n_sect = context.macho_file.getSectionOrdinal(match),
|
||||
.n_sect = match + 1,
|
||||
.n_desc = 0,
|
||||
.n_value = sect.addr,
|
||||
});
|
||||
@ -459,9 +463,10 @@ fn addPtrBindingOrRebase(
|
||||
});
|
||||
} else {
|
||||
const source_sym = self.getSymbol(context.macho_file);
|
||||
const match = context.macho_file.getMatchingSectionFromOrdinal(source_sym.n_sect);
|
||||
const sect = context.macho_file.getSection(match);
|
||||
const sect_type = sect.type_();
|
||||
const section = context.macho_file.sections.get(source_sym.n_sect - 1);
|
||||
const header = section.header;
|
||||
const segment_index = section.segment_index;
|
||||
const sect_type = header.type_();
|
||||
|
||||
const should_rebase = rebase: {
|
||||
if (rel.r_length != 3) break :rebase false;
|
||||
@ -470,12 +475,12 @@ fn addPtrBindingOrRebase(
|
||||
// that the segment is writable should be enough here.
|
||||
const is_right_segment = blk: {
|
||||
if (context.macho_file.data_segment_cmd_index) |idx| {
|
||||
if (match.seg == idx) {
|
||||
if (segment_index == idx) {
|
||||
break :blk true;
|
||||
}
|
||||
}
|
||||
if (context.macho_file.data_const_segment_cmd_index) |idx| {
|
||||
if (match.seg == idx) {
|
||||
if (segment_index == idx) {
|
||||
break :blk true;
|
||||
}
|
||||
}
|
||||
@ -565,9 +570,8 @@ pub fn resolveRelocs(self: *Atom, macho_file: *MachO) !void {
|
||||
};
|
||||
const is_tlv = is_tlv: {
|
||||
const source_sym = self.getSymbol(macho_file);
|
||||
const match = macho_file.getMatchingSectionFromOrdinal(source_sym.n_sect);
|
||||
const sect = macho_file.getSection(match);
|
||||
break :is_tlv sect.type_() == macho.S_THREAD_LOCAL_VARIABLES;
|
||||
const header = macho_file.sections.items(.header)[source_sym.n_sect - 1];
|
||||
break :is_tlv header.type_() == macho.S_THREAD_LOCAL_VARIABLES;
|
||||
};
|
||||
const target_addr = blk: {
|
||||
const target_atom = rel.getTargetAtom(macho_file) orelse {
|
||||
@ -608,10 +612,7 @@ pub fn resolveRelocs(self: *Atom, macho_file: *MachO) !void {
|
||||
return error.FailedToResolveRelocationTarget;
|
||||
}
|
||||
};
|
||||
break :base_address macho_file.getSection(.{
|
||||
.seg = macho_file.data_segment_cmd_index.?,
|
||||
.sect = sect_id,
|
||||
}).addr;
|
||||
break :base_address macho_file.sections.items(.header)[sect_id].addr;
|
||||
} else 0;
|
||||
break :blk target_sym.n_value - base_address;
|
||||
};
|
||||
|
||||
@ -252,7 +252,7 @@ pub const WriteOpts = struct {
|
||||
file: fs.File,
|
||||
exec_seg_base: u64,
|
||||
exec_seg_limit: u64,
|
||||
code_sig_cmd: macho.linkedit_data_command,
|
||||
file_size: u32,
|
||||
output_mode: std.builtin.OutputMode,
|
||||
};
|
||||
|
||||
@ -274,10 +274,9 @@ pub fn writeAdhocSignature(
|
||||
self.code_directory.inner.execSegBase = opts.exec_seg_base;
|
||||
self.code_directory.inner.execSegLimit = opts.exec_seg_limit;
|
||||
self.code_directory.inner.execSegFlags = if (opts.output_mode == .Exe) macho.CS_EXECSEG_MAIN_BINARY else 0;
|
||||
const file_size = opts.code_sig_cmd.dataoff;
|
||||
self.code_directory.inner.codeLimit = file_size;
|
||||
self.code_directory.inner.codeLimit = opts.file_size;
|
||||
|
||||
const total_pages = mem.alignForward(file_size, self.page_size) / self.page_size;
|
||||
const total_pages = mem.alignForward(opts.file_size, self.page_size) / self.page_size;
|
||||
|
||||
var buffer = try allocator.alloc(u8, self.page_size);
|
||||
defer allocator.free(buffer);
|
||||
@ -289,7 +288,10 @@ pub fn writeAdhocSignature(
|
||||
var i: usize = 0;
|
||||
while (i < total_pages) : (i += 1) {
|
||||
const fstart = i * self.page_size;
|
||||
const fsize = if (fstart + self.page_size > file_size) file_size - fstart else self.page_size;
|
||||
const fsize = if (fstart + self.page_size > opts.file_size)
|
||||
opts.file_size - fstart
|
||||
else
|
||||
self.page_size;
|
||||
const len = try opts.file.preadAll(buffer, fstart);
|
||||
assert(fsize <= len);
|
||||
|
||||
|
||||
@ -25,35 +25,18 @@ base: *MachO,
|
||||
dwarf: Dwarf,
|
||||
file: fs.File,
|
||||
|
||||
/// Table of all load commands
|
||||
load_commands: std.ArrayListUnmanaged(macho.LoadCommand) = .{},
|
||||
/// __PAGEZERO segment
|
||||
pagezero_segment_cmd_index: ?u16 = null,
|
||||
/// __TEXT segment
|
||||
text_segment_cmd_index: ?u16 = null,
|
||||
/// __DATA_CONST segment
|
||||
data_const_segment_cmd_index: ?u16 = null,
|
||||
/// __DATA segment
|
||||
data_segment_cmd_index: ?u16 = null,
|
||||
/// __LINKEDIT segment
|
||||
linkedit_segment_cmd_index: ?u16 = null,
|
||||
/// __DWARF segment
|
||||
dwarf_segment_cmd_index: ?u16 = null,
|
||||
/// Symbol table
|
||||
symtab_cmd_index: ?u16 = null,
|
||||
/// UUID load command
|
||||
uuid_cmd_index: ?u16 = null,
|
||||
segments: std.ArrayListUnmanaged(macho.segment_command_64) = .{},
|
||||
sections: std.ArrayListUnmanaged(macho.section_64) = .{},
|
||||
|
||||
/// Index into __TEXT,__text section.
|
||||
text_section_index: ?u16 = null,
|
||||
linkedit_segment_cmd_index: ?u8 = null,
|
||||
dwarf_segment_cmd_index: ?u8 = null,
|
||||
|
||||
debug_info_section_index: ?u16 = null,
|
||||
debug_abbrev_section_index: ?u16 = null,
|
||||
debug_str_section_index: ?u16 = null,
|
||||
debug_aranges_section_index: ?u16 = null,
|
||||
debug_line_section_index: ?u16 = null,
|
||||
debug_info_section_index: ?u8 = null,
|
||||
debug_abbrev_section_index: ?u8 = null,
|
||||
debug_str_section_index: ?u8 = null,
|
||||
debug_aranges_section_index: ?u8 = null,
|
||||
debug_line_section_index: ?u8 = null,
|
||||
|
||||
load_commands_dirty: bool = false,
|
||||
debug_string_table_dirty: bool = false,
|
||||
debug_abbrev_section_dirty: bool = false,
|
||||
debug_aranges_section_dirty: bool = false,
|
||||
@ -78,98 +61,40 @@ pub const Reloc = struct {
|
||||
/// You must call this function *after* `MachO.populateMissingMetadata()`
|
||||
/// has been called to get a viable debug symbols output.
|
||||
pub fn populateMissingMetadata(self: *DebugSymbols, allocator: Allocator) !void {
|
||||
if (self.uuid_cmd_index == null) {
|
||||
const base_cmd = self.base.load_commands.items[self.base.uuid_cmd_index.?];
|
||||
self.uuid_cmd_index = @intCast(u16, self.load_commands.items.len);
|
||||
try self.load_commands.append(allocator, base_cmd);
|
||||
self.load_commands_dirty = true;
|
||||
}
|
||||
|
||||
if (self.symtab_cmd_index == null) {
|
||||
self.symtab_cmd_index = @intCast(u16, self.load_commands.items.len);
|
||||
try self.load_commands.append(self.base.base.allocator, .{
|
||||
.symtab = .{
|
||||
.cmdsize = @sizeOf(macho.symtab_command),
|
||||
.symoff = 0,
|
||||
.nsyms = 0,
|
||||
.stroff = 0,
|
||||
.strsize = 0,
|
||||
},
|
||||
});
|
||||
try self.strtab.buffer.append(allocator, 0);
|
||||
self.load_commands_dirty = true;
|
||||
}
|
||||
|
||||
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 cmd = try self.copySegmentCommand(allocator, base_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 cmd = try self.copySegmentCommand(allocator, base_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 cmd = try self.copySegmentCommand(allocator, base_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 cmd = try self.copySegmentCommand(allocator, base_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;
|
||||
var cmd = try self.copySegmentCommand(allocator, base_cmd);
|
||||
self.linkedit_segment_cmd_index = @intCast(u8, self.segments.items.len);
|
||||
// TODO this needs reworking
|
||||
cmd.inner.vmsize = self.base.page_size;
|
||||
cmd.inner.fileoff = self.base.page_size;
|
||||
cmd.inner.filesize = self.base.page_size;
|
||||
try self.load_commands.append(allocator, .{ .segment = cmd });
|
||||
self.load_commands_dirty = true;
|
||||
try self.segments.append(allocator, .{
|
||||
.segname = makeStaticString("__LINKEDIT"),
|
||||
.vmaddr = self.base.page_size,
|
||||
.vmsize = self.base.page_size,
|
||||
.fileoff = self.base.page_size,
|
||||
.filesize = self.base.page_size,
|
||||
.maxprot = macho.PROT.READ,
|
||||
.initprot = macho.PROT.READ,
|
||||
.cmdsize = @sizeOf(macho.segment_command_64),
|
||||
});
|
||||
}
|
||||
|
||||
if (self.dwarf_segment_cmd_index == null) {
|
||||
self.dwarf_segment_cmd_index = @intCast(u16, self.load_commands.items.len);
|
||||
self.dwarf_segment_cmd_index = @intCast(u8, self.segments.items.len);
|
||||
|
||||
const linkedit = self.load_commands.items[self.linkedit_segment_cmd_index.?].segment;
|
||||
const linkedit = self.segments.items[self.base.linkedit_segment_cmd_index.?];
|
||||
const ideal_size: u16 = 200 + 128 + 160 + 250;
|
||||
const needed_size = mem.alignForwardGeneric(u64, padToIdeal(ideal_size), self.base.page_size);
|
||||
const fileoff = linkedit.inner.fileoff + linkedit.inner.filesize;
|
||||
const vmaddr = linkedit.inner.vmaddr + linkedit.inner.vmsize;
|
||||
const fileoff = linkedit.fileoff + linkedit.filesize;
|
||||
const vmaddr = linkedit.vmaddr + linkedit.vmsize;
|
||||
|
||||
log.debug("found __DWARF segment free space 0x{x} to 0x{x}", .{ fileoff, fileoff + needed_size });
|
||||
|
||||
try self.load_commands.append(allocator, .{
|
||||
.segment = .{
|
||||
.inner = .{
|
||||
.segname = makeStaticString("__DWARF"),
|
||||
.vmaddr = vmaddr,
|
||||
.vmsize = needed_size,
|
||||
.fileoff = fileoff,
|
||||
.filesize = needed_size,
|
||||
.cmdsize = @sizeOf(macho.segment_command_64),
|
||||
},
|
||||
},
|
||||
try self.segments.append(allocator, .{
|
||||
.segname = makeStaticString("__DWARF"),
|
||||
.vmaddr = vmaddr,
|
||||
.vmsize = needed_size,
|
||||
.fileoff = fileoff,
|
||||
.filesize = needed_size,
|
||||
.cmdsize = @sizeOf(macho.segment_command_64),
|
||||
});
|
||||
self.load_commands_dirty = true;
|
||||
}
|
||||
|
||||
if (self.debug_str_section_index == null) {
|
||||
@ -203,18 +128,18 @@ 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;
|
||||
fn allocateSection(self: *DebugSymbols, sectname: []const u8, size: u64, alignment: u16) !u8 {
|
||||
const segment = &self.segments.items[self.dwarf_segment_cmd_index.?];
|
||||
var sect = macho.section_64{
|
||||
.sectname = makeStaticString(sectname),
|
||||
.segname = seg.inner.segname,
|
||||
.segname = segment.segname,
|
||||
.size = @intCast(u32, size),
|
||||
.@"align" = alignment,
|
||||
};
|
||||
const alignment_pow_2 = try math.powi(u32, 2, alignment);
|
||||
const off = self.findFreeSpace(size, alignment_pow_2);
|
||||
|
||||
assert(off + size <= seg.inner.fileoff + seg.inner.filesize); // TODO expand
|
||||
assert(off + size <= segment.fileoff + segment.filesize); // TODO expand
|
||||
|
||||
log.debug("found {s},{s} section free space 0x{x} to 0x{x}", .{
|
||||
sect.segName(),
|
||||
@ -223,31 +148,20 @@ fn allocateSection(self: *DebugSymbols, sectname: []const u8, size: u64, alignme
|
||||
off + size,
|
||||
});
|
||||
|
||||
sect.addr = seg.inner.vmaddr + off - seg.inner.fileoff;
|
||||
sect.addr = segment.vmaddr + off - segment.fileoff;
|
||||
sect.offset = @intCast(u32, off);
|
||||
|
||||
const index = @intCast(u16, seg.sections.items.len);
|
||||
try seg.sections.append(self.base.base.allocator, sect);
|
||||
seg.inner.cmdsize += @sizeOf(macho.section_64);
|
||||
seg.inner.nsects += 1;
|
||||
|
||||
// TODO
|
||||
// const match = MatchingSection{
|
||||
// .seg = segment_id,
|
||||
// .sect = index,
|
||||
// };
|
||||
// _ = try self.section_ordinals.getOrPut(self.base.allocator, match);
|
||||
// try self.block_free_lists.putNoClobber(self.base.allocator, match, .{});
|
||||
|
||||
self.load_commands_dirty = true;
|
||||
const index = @intCast(u8, self.sections.items.len);
|
||||
try self.sections.append(self.base.base.allocator, sect);
|
||||
segment.cmdsize += @sizeOf(macho.section_64);
|
||||
segment.nsects += 1;
|
||||
|
||||
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| {
|
||||
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) {
|
||||
@ -258,8 +172,8 @@ fn detectAllocCollision(self: *DebugSymbols, start: u64, size: u64) ?u64 {
|
||||
}
|
||||
|
||||
pub 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;
|
||||
const segment = self.segments.items[self.dwarf_segment_cmd_index.?];
|
||||
var offset: u64 = segment.fileoff;
|
||||
while (self.detectAllocCollision(offset, object_size)) |item_end| {
|
||||
offset = mem.alignForwardGeneric(u64, item_end, min_alignment);
|
||||
}
|
||||
@ -296,8 +210,7 @@ pub fn flushModule(self: *DebugSymbols, allocator: Allocator, options: link.Opti
|
||||
break :blk got_entry.getName(self.base);
|
||||
},
|
||||
};
|
||||
const seg = &self.load_commands.items[self.dwarf_segment_cmd_index.?].segment;
|
||||
const sect = &seg.sections.items[self.debug_info_section_index.?];
|
||||
const sect = &self.sections.items[self.debug_info_section_index.?];
|
||||
const file_offset = sect.offset + reloc.offset;
|
||||
log.debug("resolving relocation: {d}@{x} ('{s}') at offset {x}", .{
|
||||
reloc.target,
|
||||
@ -311,15 +224,13 @@ pub fn flushModule(self: *DebugSymbols, allocator: Allocator, options: link.Opti
|
||||
|
||||
if (self.debug_abbrev_section_dirty) {
|
||||
try self.dwarf.writeDbgAbbrev(&self.base.base);
|
||||
self.load_commands_dirty = true;
|
||||
self.debug_abbrev_section_dirty = false;
|
||||
}
|
||||
|
||||
if (self.debug_info_header_dirty) {
|
||||
// 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_section = text_segment.sections.items[self.text_section_index.?];
|
||||
const text_section = self.base.sections.items(.header)[self.base.text_section_index.?];
|
||||
const low_pc = text_section.addr;
|
||||
const high_pc = text_section.addr + text_section.size;
|
||||
try self.dwarf.writeDbgInfoHeader(&self.base.base, module, low_pc, high_pc);
|
||||
@ -329,10 +240,8 @@ pub fn flushModule(self: *DebugSymbols, allocator: Allocator, options: link.Opti
|
||||
if (self.debug_aranges_section_dirty) {
|
||||
// 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_section = text_segment.sections.items[self.text_section_index.?];
|
||||
const text_section = self.base.sections.items(.header)[self.base.text_section_index.?];
|
||||
try self.dwarf.writeDbgAranges(&self.base.base, text_section.addr, text_section.size);
|
||||
self.load_commands_dirty = true;
|
||||
self.debug_aranges_section_dirty = false;
|
||||
}
|
||||
|
||||
@ -342,8 +251,8 @@ pub fn flushModule(self: *DebugSymbols, allocator: Allocator, options: link.Opti
|
||||
}
|
||||
|
||||
{
|
||||
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.?];
|
||||
const dwarf_segment = &self.segments.items[self.dwarf_segment_cmd_index.?];
|
||||
const debug_strtab_sect = &self.sections.items[self.debug_str_section_index.?];
|
||||
if (self.debug_string_table_dirty or self.dwarf.strtab.items.len != debug_strtab_sect.size) {
|
||||
const allocated_size = self.allocatedSize(debug_strtab_sect.offset);
|
||||
const needed_size = self.dwarf.strtab.items.len;
|
||||
@ -351,7 +260,7 @@ pub fn flushModule(self: *DebugSymbols, allocator: Allocator, options: link.Opti
|
||||
if (needed_size > allocated_size) {
|
||||
debug_strtab_sect.size = 0; // free the space
|
||||
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.addr = dwarf_segment.vmaddr + new_offset - dwarf_segment.fileoff;
|
||||
debug_strtab_sect.offset = @intCast(u32, new_offset);
|
||||
}
|
||||
debug_strtab_sect.size = @intCast(u32, needed_size);
|
||||
@ -362,28 +271,53 @@ pub fn flushModule(self: *DebugSymbols, allocator: Allocator, options: link.Opti
|
||||
});
|
||||
|
||||
try self.file.pwriteAll(self.dwarf.strtab.items, debug_strtab_sect.offset);
|
||||
self.load_commands_dirty = true;
|
||||
self.debug_string_table_dirty = false;
|
||||
}
|
||||
}
|
||||
|
||||
self.updateDwarfSegment();
|
||||
try self.writeLinkeditSegment();
|
||||
try self.updateVirtualMemoryMapping();
|
||||
try self.writeLoadCommands(allocator);
|
||||
try self.writeHeader();
|
||||
var lc_buffer = std.ArrayList(u8).init(allocator);
|
||||
defer lc_buffer.deinit();
|
||||
const lc_writer = lc_buffer.writer();
|
||||
var ncmds: u32 = 0;
|
||||
|
||||
try self.writeLinkeditSegmentData(&ncmds, lc_writer);
|
||||
self.updateDwarfSegment();
|
||||
|
||||
{
|
||||
try lc_writer.writeStruct(self.base.uuid);
|
||||
ncmds += 1;
|
||||
}
|
||||
|
||||
var headers_buf = std.ArrayList(u8).init(allocator);
|
||||
defer headers_buf.deinit();
|
||||
try self.base.writeSegmentHeaders(
|
||||
0,
|
||||
self.base.linkedit_segment_cmd_index.?,
|
||||
&ncmds,
|
||||
headers_buf.writer(),
|
||||
);
|
||||
|
||||
for (self.segments.items) |seg| {
|
||||
try headers_buf.writer().writeStruct(seg);
|
||||
ncmds += 2;
|
||||
}
|
||||
for (self.sections.items) |header| {
|
||||
try headers_buf.writer().writeStruct(header);
|
||||
}
|
||||
|
||||
try self.file.pwriteAll(headers_buf.items, @sizeOf(macho.mach_header_64));
|
||||
try self.file.pwriteAll(lc_buffer.items, @sizeOf(macho.mach_header_64) + headers_buf.items.len);
|
||||
|
||||
try self.writeHeader(ncmds, @intCast(u32, lc_buffer.items.len + headers_buf.items.len));
|
||||
|
||||
assert(!self.load_commands_dirty);
|
||||
assert(!self.debug_abbrev_section_dirty);
|
||||
assert(!self.debug_aranges_section_dirty);
|
||||
assert(!self.debug_string_table_dirty);
|
||||
}
|
||||
|
||||
pub fn deinit(self: *DebugSymbols, allocator: Allocator) void {
|
||||
for (self.load_commands.items) |*lc| {
|
||||
lc.deinit(allocator);
|
||||
}
|
||||
self.load_commands.deinit(allocator);
|
||||
self.segments.deinit(allocator);
|
||||
self.sections.deinit(allocator);
|
||||
self.dwarf.deinit();
|
||||
self.strtab.deinit(allocator);
|
||||
self.relocs.deinit(allocator);
|
||||
@ -402,59 +336,19 @@ pub fn swapRemoveRelocs(self: *DebugSymbols, target: u32) void {
|
||||
}
|
||||
}
|
||||
|
||||
fn copySegmentCommand(
|
||||
self: *DebugSymbols,
|
||||
allocator: Allocator,
|
||||
base_cmd: macho.SegmentCommand,
|
||||
) !macho.SegmentCommand {
|
||||
var cmd = macho.SegmentCommand{
|
||||
.inner = .{
|
||||
.segname = undefined,
|
||||
.cmdsize = base_cmd.inner.cmdsize,
|
||||
.vmaddr = base_cmd.inner.vmaddr,
|
||||
.vmsize = base_cmd.inner.vmsize,
|
||||
.maxprot = base_cmd.inner.maxprot,
|
||||
.initprot = base_cmd.inner.initprot,
|
||||
.nsects = base_cmd.inner.nsects,
|
||||
.flags = base_cmd.inner.flags,
|
||||
},
|
||||
};
|
||||
mem.copy(u8, &cmd.inner.segname, &base_cmd.inner.segname);
|
||||
fn updateDwarfSegment(self: *DebugSymbols) void {
|
||||
const linkedit = self.segments.items[self.linkedit_segment_cmd_index.?];
|
||||
const dwarf_segment = &self.segments.items[self.dwarf_segment_cmd_index.?];
|
||||
|
||||
try cmd.sections.ensureTotalCapacity(allocator, cmd.inner.nsects);
|
||||
for (base_cmd.sections.items) |base_sect, i| {
|
||||
var sect = macho.section_64{
|
||||
.sectname = undefined,
|
||||
.segname = undefined,
|
||||
.addr = base_sect.addr,
|
||||
.size = base_sect.size,
|
||||
.offset = 0,
|
||||
.@"align" = base_sect.@"align",
|
||||
.reloff = 0,
|
||||
.nreloc = 0,
|
||||
.flags = base_sect.flags,
|
||||
.reserved1 = base_sect.reserved1,
|
||||
.reserved2 = base_sect.reserved2,
|
||||
.reserved3 = base_sect.reserved3,
|
||||
};
|
||||
mem.copy(u8, §.sectname, &base_sect.sectname);
|
||||
mem.copy(u8, §.segname, &base_sect.segname);
|
||||
|
||||
if (self.base.text_section_index.? == i) {
|
||||
self.text_section_index = @intCast(u16, i);
|
||||
}
|
||||
|
||||
cmd.sections.appendAssumeCapacity(sect);
|
||||
const new_start_aligned = linkedit.vmaddr + linkedit.vmsize;
|
||||
const old_start_aligned = dwarf_segment.vmaddr;
|
||||
const diff = new_start_aligned - old_start_aligned;
|
||||
if (diff > 0) {
|
||||
dwarf_segment.vmaddr = new_start_aligned;
|
||||
}
|
||||
|
||||
return cmd;
|
||||
}
|
||||
|
||||
fn updateDwarfSegment(self: *DebugSymbols) void {
|
||||
const dwarf_segment = &self.load_commands.items[self.dwarf_segment_cmd_index.?].segment;
|
||||
|
||||
var max_offset: u64 = 0;
|
||||
for (dwarf_segment.sections.items) |sect| {
|
||||
for (self.sections.items) |*sect| {
|
||||
log.debug(" {s},{s} - 0x{x}-0x{x} - 0x{x}-0x{x}", .{
|
||||
sect.segName(),
|
||||
sect.sectName(),
|
||||
@ -466,44 +360,19 @@ fn updateDwarfSegment(self: *DebugSymbols) void {
|
||||
if (sect.offset + sect.size > max_offset) {
|
||||
max_offset = sect.offset + sect.size;
|
||||
}
|
||||
sect.addr += diff;
|
||||
}
|
||||
|
||||
const file_size = max_offset - dwarf_segment.inner.fileoff;
|
||||
const file_size = max_offset - dwarf_segment.fileoff;
|
||||
log.debug("__DWARF size 0x{x}", .{file_size});
|
||||
|
||||
if (file_size != dwarf_segment.inner.filesize) {
|
||||
dwarf_segment.inner.filesize = file_size;
|
||||
if (dwarf_segment.inner.vmsize < dwarf_segment.inner.filesize) {
|
||||
dwarf_segment.inner.vmsize = mem.alignForwardGeneric(u64, dwarf_segment.inner.filesize, self.base.page_size);
|
||||
}
|
||||
self.load_commands_dirty = true;
|
||||
if (file_size != dwarf_segment.filesize) {
|
||||
dwarf_segment.filesize = file_size;
|
||||
dwarf_segment.vmsize = mem.alignForwardGeneric(u64, dwarf_segment.filesize, self.base.page_size);
|
||||
}
|
||||
}
|
||||
|
||||
/// Writes all load commands and section headers.
|
||||
fn writeLoadCommands(self: *DebugSymbols, allocator: Allocator) !void {
|
||||
if (!self.load_commands_dirty) return;
|
||||
|
||||
var sizeofcmds: u32 = 0;
|
||||
for (self.load_commands.items) |lc| {
|
||||
sizeofcmds += lc.cmdsize();
|
||||
}
|
||||
|
||||
var buffer = try allocator.alloc(u8, sizeofcmds);
|
||||
defer allocator.free(buffer);
|
||||
var fib = std.io.fixedBufferStream(buffer);
|
||||
const writer = fib.writer();
|
||||
for (self.load_commands.items) |lc| {
|
||||
try lc.write(writer);
|
||||
}
|
||||
|
||||
const off = @sizeOf(macho.mach_header_64);
|
||||
log.debug("writing {} load commands from 0x{x} to 0x{x}", .{ self.load_commands.items.len, off, off + sizeofcmds });
|
||||
try self.file.pwriteAll(buffer, off);
|
||||
self.load_commands_dirty = false;
|
||||
}
|
||||
|
||||
fn writeHeader(self: *DebugSymbols) !void {
|
||||
fn writeHeader(self: *DebugSymbols, ncmds: u32, sizeofcmds: u32) !void {
|
||||
var header: macho.mach_header_64 = .{};
|
||||
header.filetype = macho.MH_DSYM;
|
||||
|
||||
@ -519,12 +388,8 @@ fn writeHeader(self: *DebugSymbols) !void {
|
||||
else => return error.UnsupportedCpuArchitecture,
|
||||
}
|
||||
|
||||
header.ncmds = @intCast(u32, self.load_commands.items.len);
|
||||
header.sizeofcmds = 0;
|
||||
|
||||
for (self.load_commands.items) |cmd| {
|
||||
header.sizeofcmds += cmd.cmdsize();
|
||||
}
|
||||
header.ncmds = ncmds;
|
||||
header.sizeofcmds = sizeofcmds;
|
||||
|
||||
log.debug("writing Mach-O header {}", .{header});
|
||||
|
||||
@ -532,79 +397,46 @@ fn writeHeader(self: *DebugSymbols) !void {
|
||||
}
|
||||
|
||||
pub fn allocatedSize(self: *DebugSymbols, start: u64) u64 {
|
||||
const seg = self.load_commands.items[self.dwarf_segment_cmd_index.?].segment;
|
||||
assert(start >= seg.inner.fileoff);
|
||||
const seg = self.segments.items[self.dwarf_segment_cmd_index.?];
|
||||
assert(start >= seg.fileoff);
|
||||
var min_pos: u64 = std.math.maxInt(u64);
|
||||
for (seg.sections.items) |section| {
|
||||
for (self.sections.items) |section| {
|
||||
if (section.offset <= start) continue;
|
||||
if (section.offset < min_pos) min_pos = section.offset;
|
||||
}
|
||||
return min_pos - start;
|
||||
}
|
||||
|
||||
fn updateVirtualMemoryMapping(self: *DebugSymbols) !void {
|
||||
const macho_file = self.base;
|
||||
const allocator = macho_file.base.allocator;
|
||||
|
||||
const IndexTuple = std.meta.Tuple(&[_]type{ *?u16, *?u16 });
|
||||
const indices = &[_]IndexTuple{
|
||||
.{ &macho_file.text_segment_cmd_index, &self.text_segment_cmd_index },
|
||||
.{ &macho_file.data_const_segment_cmd_index, &self.data_const_segment_cmd_index },
|
||||
.{ &macho_file.data_segment_cmd_index, &self.data_segment_cmd_index },
|
||||
};
|
||||
|
||||
for (indices) |tuple| {
|
||||
const orig_cmd = macho_file.load_commands.items[tuple[0].*.?].segment;
|
||||
const cmd = try self.copySegmentCommand(allocator, orig_cmd);
|
||||
const comp_cmd = &self.load_commands.items[tuple[1].*.?];
|
||||
comp_cmd.deinit(allocator);
|
||||
self.load_commands.items[tuple[1].*.?] = .{ .segment = cmd };
|
||||
}
|
||||
|
||||
// TODO should we set the linkedit vmsize to that of the binary?
|
||||
const orig_cmd = macho_file.load_commands.items[macho_file.linkedit_segment_cmd_index.?].segment;
|
||||
const orig_vmaddr = orig_cmd.inner.vmaddr;
|
||||
const linkedit_cmd = &self.load_commands.items[self.linkedit_segment_cmd_index.?].segment;
|
||||
linkedit_cmd.inner.vmaddr = orig_vmaddr;
|
||||
|
||||
// Update VM address for the DWARF segment and sections including re-running relocations.
|
||||
// TODO re-run relocations
|
||||
const dwarf_cmd = &self.load_commands.items[self.dwarf_segment_cmd_index.?].segment;
|
||||
const new_start_aligned = orig_vmaddr + linkedit_cmd.inner.vmsize;
|
||||
const old_start_aligned = dwarf_cmd.inner.vmaddr;
|
||||
const diff = new_start_aligned - old_start_aligned;
|
||||
if (diff > 0) {
|
||||
dwarf_cmd.inner.vmaddr = new_start_aligned;
|
||||
|
||||
for (dwarf_cmd.sections.items) |*sect| {
|
||||
sect.addr += (new_start_aligned - old_start_aligned);
|
||||
}
|
||||
}
|
||||
|
||||
self.load_commands_dirty = true;
|
||||
}
|
||||
|
||||
fn writeLinkeditSegment(self: *DebugSymbols) !void {
|
||||
fn writeLinkeditSegmentData(self: *DebugSymbols, ncmds: *u32, lc_writer: anytype) !void {
|
||||
const tracy = trace(@src());
|
||||
defer tracy.end();
|
||||
|
||||
try self.writeSymbolTable();
|
||||
try self.writeStringTable();
|
||||
const source_vmaddr = self.base.segments.items[self.base.linkedit_segment_cmd_index.?].vmaddr;
|
||||
const seg = &self.segments.items[self.linkedit_segment_cmd_index.?];
|
||||
seg.vmaddr = source_vmaddr;
|
||||
|
||||
const seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].segment;
|
||||
const aligned_size = mem.alignForwardGeneric(u64, seg.inner.filesize, self.base.page_size);
|
||||
seg.inner.filesize = aligned_size;
|
||||
seg.inner.vmsize = aligned_size;
|
||||
var symtab_cmd = macho.symtab_command{
|
||||
.cmdsize = @sizeOf(macho.symtab_command),
|
||||
.symoff = 0,
|
||||
.nsyms = 0,
|
||||
.stroff = 0,
|
||||
.strsize = 0,
|
||||
};
|
||||
try self.writeSymtab(&symtab_cmd);
|
||||
try self.writeStrtab(&symtab_cmd);
|
||||
try lc_writer.writeStruct(symtab_cmd);
|
||||
ncmds.* += 1;
|
||||
|
||||
const aligned_size = mem.alignForwardGeneric(u64, seg.filesize, self.base.page_size);
|
||||
seg.filesize = aligned_size;
|
||||
seg.vmsize = aligned_size;
|
||||
}
|
||||
|
||||
fn writeSymbolTable(self: *DebugSymbols) !void {
|
||||
fn writeSymtab(self: *DebugSymbols, lc: *macho.symtab_command) !void {
|
||||
const tracy = trace(@src());
|
||||
defer tracy.end();
|
||||
|
||||
const gpa = self.base.base.allocator;
|
||||
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);
|
||||
|
||||
var locals = std.ArrayList(macho.nlist_64).init(gpa);
|
||||
defer locals.deinit();
|
||||
@ -634,34 +466,36 @@ fn writeSymbolTable(self: *DebugSymbols) !void {
|
||||
|
||||
const nlocals = locals.items.len;
|
||||
const nexports = exports.items.len;
|
||||
const locals_off = symtab.symoff;
|
||||
const locals_size = nlocals * @sizeOf(macho.nlist_64);
|
||||
const exports_off = locals_off + locals_size;
|
||||
const exports_size = nexports * @sizeOf(macho.nlist_64);
|
||||
const nsyms = nlocals + nexports;
|
||||
|
||||
symtab.nsyms = @intCast(u32, nlocals + nexports);
|
||||
const needed_size = (nlocals + nexports) * @sizeOf(macho.nlist_64);
|
||||
const seg = &self.segments.items[self.linkedit_segment_cmd_index.?];
|
||||
const offset = mem.alignForwardGeneric(
|
||||
u64,
|
||||
seg.fileoff + seg.filesize,
|
||||
@alignOf(macho.nlist_64),
|
||||
);
|
||||
const needed_size = nsyms * @sizeOf(macho.nlist_64);
|
||||
|
||||
if (needed_size > seg.inner.filesize) {
|
||||
if (needed_size > seg.filesize) {
|
||||
const aligned_size = mem.alignForwardGeneric(u64, needed_size, self.base.page_size);
|
||||
const diff = @intCast(u32, aligned_size - seg.inner.filesize);
|
||||
const dwarf_seg = &self.load_commands.items[self.dwarf_segment_cmd_index.?].segment;
|
||||
seg.inner.filesize = aligned_size;
|
||||
const diff = @intCast(u32, aligned_size - seg.filesize);
|
||||
const dwarf_seg = &self.segments.items[self.dwarf_segment_cmd_index.?];
|
||||
seg.filesize = aligned_size;
|
||||
|
||||
try MachO.copyRangeAllOverlappingAlloc(
|
||||
self.base.base.allocator,
|
||||
self.file,
|
||||
dwarf_seg.inner.fileoff,
|
||||
dwarf_seg.inner.fileoff + diff,
|
||||
math.cast(usize, dwarf_seg.inner.filesize) orelse return error.Overflow,
|
||||
dwarf_seg.fileoff,
|
||||
dwarf_seg.fileoff + diff,
|
||||
math.cast(usize, dwarf_seg.filesize) orelse return error.Overflow,
|
||||
);
|
||||
|
||||
const old_seg_fileoff = dwarf_seg.inner.fileoff;
|
||||
dwarf_seg.inner.fileoff += diff;
|
||||
const old_seg_fileoff = dwarf_seg.fileoff;
|
||||
dwarf_seg.fileoff += diff;
|
||||
|
||||
log.debug(" (moving __DWARF segment from 0x{x} to 0x{x})", .{ old_seg_fileoff, dwarf_seg.inner.fileoff });
|
||||
log.debug(" (moving __DWARF segment from 0x{x} to 0x{x})", .{ old_seg_fileoff, dwarf_seg.fileoff });
|
||||
|
||||
for (dwarf_seg.sections.items) |*sect| {
|
||||
for (self.sections.items) |*sect| {
|
||||
const old_offset = sect.offset;
|
||||
sect.offset += diff;
|
||||
|
||||
@ -674,47 +508,53 @@ fn writeSymbolTable(self: *DebugSymbols) !void {
|
||||
}
|
||||
}
|
||||
|
||||
lc.symoff = @intCast(u32, offset);
|
||||
lc.nsyms = @intCast(u32, nsyms);
|
||||
|
||||
const locals_off = lc.symoff;
|
||||
const locals_size = nlocals * @sizeOf(macho.nlist_64);
|
||||
const exports_off = locals_off + locals_size;
|
||||
const exports_size = nexports * @sizeOf(macho.nlist_64);
|
||||
|
||||
log.debug("writing local symbols from 0x{x} to 0x{x}", .{ locals_off, locals_size + locals_off });
|
||||
try self.file.pwriteAll(mem.sliceAsBytes(locals.items), locals_off);
|
||||
|
||||
log.debug("writing exported symbols from 0x{x} to 0x{x}", .{ exports_off, exports_size + exports_off });
|
||||
try self.file.pwriteAll(mem.sliceAsBytes(exports.items), exports_off);
|
||||
|
||||
self.load_commands_dirty = true;
|
||||
}
|
||||
|
||||
fn writeStringTable(self: *DebugSymbols) !void {
|
||||
fn writeStrtab(self: *DebugSymbols, lc: *macho.symtab_command) !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 symtab_size = @intCast(u32, symtab.nsyms * @sizeOf(macho.nlist_64));
|
||||
symtab.stroff = symtab.symoff + symtab_size;
|
||||
const seg = &self.segments.items[self.linkedit_segment_cmd_index.?];
|
||||
const symtab_size = @intCast(u32, lc.nsyms * @sizeOf(macho.nlist_64));
|
||||
const offset = mem.alignForwardGeneric(u64, lc.symoff + symtab_size, @alignOf(u64));
|
||||
lc.stroff = @intCast(u32, offset);
|
||||
|
||||
const needed_size = mem.alignForwardGeneric(u64, self.strtab.buffer.items.len, @alignOf(u64));
|
||||
symtab.strsize = @intCast(u32, needed_size);
|
||||
lc.strsize = @intCast(u32, needed_size);
|
||||
|
||||
if (symtab_size + needed_size > seg.inner.filesize) {
|
||||
const aligned_size = mem.alignForwardGeneric(u64, symtab_size + needed_size, self.base.page_size);
|
||||
const diff = @intCast(u32, aligned_size - seg.inner.filesize);
|
||||
const dwarf_seg = &self.load_commands.items[self.dwarf_segment_cmd_index.?].segment;
|
||||
seg.inner.filesize = aligned_size;
|
||||
if (offset + needed_size > seg.filesize) {
|
||||
const aligned_size = mem.alignForwardGeneric(u64, offset + needed_size, self.base.page_size);
|
||||
const diff = @intCast(u32, aligned_size - seg.filesize);
|
||||
const dwarf_seg = &self.segments.items[self.dwarf_segment_cmd_index.?];
|
||||
seg.filesize = aligned_size;
|
||||
|
||||
try MachO.copyRangeAllOverlappingAlloc(
|
||||
self.base.base.allocator,
|
||||
self.file,
|
||||
dwarf_seg.inner.fileoff,
|
||||
dwarf_seg.inner.fileoff + diff,
|
||||
math.cast(usize, dwarf_seg.inner.filesize) orelse return error.Overflow,
|
||||
dwarf_seg.fileoff,
|
||||
dwarf_seg.fileoff + diff,
|
||||
math.cast(usize, dwarf_seg.filesize) orelse return error.Overflow,
|
||||
);
|
||||
|
||||
const old_seg_fileoff = dwarf_seg.inner.fileoff;
|
||||
dwarf_seg.inner.fileoff += diff;
|
||||
const old_seg_fileoff = dwarf_seg.fileoff;
|
||||
dwarf_seg.fileoff += diff;
|
||||
|
||||
log.debug(" (moving __DWARF segment from 0x{x} to 0x{x})", .{ old_seg_fileoff, dwarf_seg.inner.fileoff });
|
||||
log.debug(" (moving __DWARF segment from 0x{x} to 0x{x})", .{ old_seg_fileoff, dwarf_seg.fileoff });
|
||||
|
||||
for (dwarf_seg.sections.items) |*sect| {
|
||||
for (self.sections.items) |*sect| {
|
||||
const old_offset = sect.offset;
|
||||
sect.offset += diff;
|
||||
|
||||
@ -727,9 +567,7 @@ fn writeStringTable(self: *DebugSymbols) !void {
|
||||
}
|
||||
}
|
||||
|
||||
log.debug("writing string table from 0x{x} to 0x{x}", .{ symtab.stroff, symtab.stroff + symtab.strsize });
|
||||
log.debug("writing string table from 0x{x} to 0x{x}", .{ lc.stroff, lc.stroff + lc.strsize });
|
||||
|
||||
try self.file.pwriteAll(self.strtab.buffer.items, symtab.stroff);
|
||||
|
||||
self.load_commands_dirty = true;
|
||||
try self.file.pwriteAll(self.strtab.buffer.items, lc.stroff);
|
||||
}
|
||||
|
||||
@ -13,23 +13,9 @@ const fat = @import("fat.zig");
|
||||
const Allocator = mem.Allocator;
|
||||
const CrossTarget = std.zig.CrossTarget;
|
||||
const LibStub = @import("../tapi.zig").LibStub;
|
||||
const LoadCommandIterator = macho.LoadCommandIterator;
|
||||
const MachO = @import("../MachO.zig");
|
||||
|
||||
file: fs.File,
|
||||
name: []const u8,
|
||||
|
||||
header: ?macho.mach_header_64 = null,
|
||||
|
||||
// The actual dylib contents we care about linking with will be embedded at
|
||||
// an offset within a file if we are linking against a fat lib
|
||||
library_offset: u64 = 0,
|
||||
|
||||
load_commands: std.ArrayListUnmanaged(macho.LoadCommand) = .{},
|
||||
|
||||
symtab_cmd_index: ?u16 = null,
|
||||
dysymtab_cmd_index: ?u16 = null,
|
||||
id_cmd_index: ?u16 = null,
|
||||
|
||||
id: ?Id = null,
|
||||
weak: bool = false,
|
||||
|
||||
@ -53,16 +39,12 @@ pub const Id = struct {
|
||||
};
|
||||
}
|
||||
|
||||
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));
|
||||
|
||||
pub fn fromLoadCommand(allocator: Allocator, lc: macho.dylib_command, name: []const u8) !Id {
|
||||
return Id{
|
||||
.name = name,
|
||||
.timestamp = dylib.timestamp,
|
||||
.current_version = dylib.current_version,
|
||||
.compatibility_version = dylib.compatibility_version,
|
||||
.name = try allocator.dupe(u8, name),
|
||||
.timestamp = lc.dylib.timestamp,
|
||||
.current_version = lc.dylib.current_version,
|
||||
.compatibility_version = lc.dylib.compatibility_version,
|
||||
};
|
||||
}
|
||||
|
||||
@ -126,125 +108,89 @@ pub const Id = struct {
|
||||
};
|
||||
|
||||
pub fn deinit(self: *Dylib, allocator: Allocator) void {
|
||||
for (self.load_commands.items) |*lc| {
|
||||
lc.deinit(allocator);
|
||||
}
|
||||
self.load_commands.deinit(allocator);
|
||||
|
||||
for (self.symbols.keys()) |key| {
|
||||
allocator.free(key);
|
||||
}
|
||||
self.symbols.deinit(allocator);
|
||||
|
||||
allocator.free(self.name);
|
||||
|
||||
if (self.id) |*id| {
|
||||
id.deinit(allocator);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn parse(
|
||||
pub fn parseFromBinary(
|
||||
self: *Dylib,
|
||||
allocator: Allocator,
|
||||
cpu_arch: std.Target.Cpu.Arch,
|
||||
dylib_id: u16,
|
||||
dependent_libs: anytype,
|
||||
name: []const u8,
|
||||
data: []align(@alignOf(u64)) const u8,
|
||||
) !void {
|
||||
log.debug("parsing shared library '{s}'", .{self.name});
|
||||
var stream = std.io.fixedBufferStream(data);
|
||||
const reader = stream.reader();
|
||||
|
||||
self.library_offset = try fat.getLibraryOffset(self.file.reader(), cpu_arch);
|
||||
log.debug("parsing shared library '{s}'", .{name});
|
||||
|
||||
try self.file.seekTo(self.library_offset);
|
||||
const header = try reader.readStruct(macho.mach_header_64);
|
||||
|
||||
var reader = self.file.reader();
|
||||
self.header = try reader.readStruct(macho.mach_header_64);
|
||||
|
||||
if (self.header.?.filetype != macho.MH_DYLIB) {
|
||||
log.debug("invalid filetype: expected 0x{x}, found 0x{x}", .{ macho.MH_DYLIB, self.header.?.filetype });
|
||||
if (header.filetype != macho.MH_DYLIB) {
|
||||
log.debug("invalid filetype: expected 0x{x}, found 0x{x}", .{ macho.MH_DYLIB, header.filetype });
|
||||
return error.NotDylib;
|
||||
}
|
||||
|
||||
const this_arch: std.Target.Cpu.Arch = try fat.decodeArch(self.header.?.cputype, true);
|
||||
const this_arch: std.Target.Cpu.Arch = try fat.decodeArch(header.cputype, true);
|
||||
|
||||
if (this_arch != cpu_arch) {
|
||||
log.err("mismatched cpu architecture: expected {}, found {}", .{ cpu_arch, this_arch });
|
||||
log.err("mismatched cpu architecture: expected {s}, found {s}", .{
|
||||
@tagName(cpu_arch),
|
||||
@tagName(this_arch),
|
||||
});
|
||||
return error.MismatchedCpuArchitecture;
|
||||
}
|
||||
|
||||
try self.readLoadCommands(allocator, reader, dylib_id, dependent_libs);
|
||||
try self.parseId(allocator);
|
||||
try self.parseSymbols(allocator);
|
||||
}
|
||||
|
||||
fn readLoadCommands(
|
||||
self: *Dylib,
|
||||
allocator: Allocator,
|
||||
reader: anytype,
|
||||
dylib_id: u16,
|
||||
dependent_libs: anytype,
|
||||
) !void {
|
||||
const should_lookup_reexports = self.header.?.flags & macho.MH_NO_REEXPORTED_DYLIBS == 0;
|
||||
|
||||
try self.load_commands.ensureUnusedCapacity(allocator, self.header.?.ncmds);
|
||||
|
||||
var i: u16 = 0;
|
||||
while (i < self.header.?.ncmds) : (i += 1) {
|
||||
var cmd = try macho.LoadCommand.read(allocator, reader);
|
||||
const should_lookup_reexports = header.flags & macho.MH_NO_REEXPORTED_DYLIBS == 0;
|
||||
var it = LoadCommandIterator{
|
||||
.ncmds = header.ncmds,
|
||||
.buffer = data[@sizeOf(macho.mach_header_64)..][0..header.sizeofcmds],
|
||||
};
|
||||
while (it.next()) |cmd| {
|
||||
switch (cmd.cmd()) {
|
||||
.SYMTAB => {
|
||||
self.symtab_cmd_index = i;
|
||||
},
|
||||
.DYSYMTAB => {
|
||||
self.dysymtab_cmd_index = i;
|
||||
const symtab_cmd = cmd.cast(macho.symtab_command).?;
|
||||
const symtab = @ptrCast(
|
||||
[*]const macho.nlist_64,
|
||||
@alignCast(@alignOf(macho.nlist_64), &data[symtab_cmd.symoff]),
|
||||
)[0..symtab_cmd.nsyms];
|
||||
const strtab = data[symtab_cmd.stroff..][0..symtab_cmd.strsize];
|
||||
|
||||
for (symtab) |sym| {
|
||||
const add_to_symtab = sym.ext() and (sym.sect() or sym.indr());
|
||||
if (!add_to_symtab) continue;
|
||||
|
||||
const sym_name = mem.sliceTo(@ptrCast([*:0]const u8, strtab.ptr + sym.n_strx), 0);
|
||||
try self.symbols.putNoClobber(allocator, try allocator.dupe(u8, sym_name), {});
|
||||
}
|
||||
},
|
||||
.ID_DYLIB => {
|
||||
self.id_cmd_index = i;
|
||||
self.id = try Id.fromLoadCommand(
|
||||
allocator,
|
||||
cmd.cast(macho.dylib_command).?,
|
||||
cmd.getDylibPathName(),
|
||||
);
|
||||
},
|
||||
.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.cast(macho.dylib_command).?,
|
||||
cmd.getDylibPathName(),
|
||||
);
|
||||
try dependent_libs.writeItem(.{ .id = id, .parent = dylib_id });
|
||||
}
|
||||
},
|
||||
else => {
|
||||
log.debug("Unknown load command detected: 0x{x}.", .{@enumToInt(cmd.cmd())});
|
||||
},
|
||||
else => {},
|
||||
}
|
||||
self.load_commands.appendAssumeCapacity(cmd);
|
||||
}
|
||||
}
|
||||
|
||||
fn parseId(self: *Dylib, allocator: Allocator) !void {
|
||||
const index = self.id_cmd_index orelse {
|
||||
log.debug("no LC_ID_DYLIB load command found; using hard-coded defaults...", .{});
|
||||
self.id = try Id.default(allocator, self.name);
|
||||
return;
|
||||
};
|
||||
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 = try allocator.alloc(u8, @sizeOf(macho.nlist_64) * symtab_cmd.nsyms);
|
||||
defer allocator.free(symtab);
|
||||
_ = try self.file.preadAll(symtab, symtab_cmd.symoff + self.library_offset);
|
||||
const slice = @alignCast(@alignOf(macho.nlist_64), mem.bytesAsSlice(macho.nlist_64, symtab));
|
||||
|
||||
const strtab = try allocator.alloc(u8, symtab_cmd.strsize);
|
||||
defer allocator.free(strtab);
|
||||
_ = try self.file.preadAll(strtab, symtab_cmd.stroff + self.library_offset);
|
||||
|
||||
for (slice) |sym| {
|
||||
const add_to_symtab = sym.ext() and (sym.sect() or sym.indr());
|
||||
|
||||
if (!add_to_symtab) continue;
|
||||
|
||||
const sym_name = mem.sliceTo(@ptrCast([*:0]const u8, strtab.ptr + sym.n_strx), 0);
|
||||
const name = try allocator.dupe(u8, sym_name);
|
||||
try self.symbols.putNoClobber(allocator, name, {});
|
||||
}
|
||||
}
|
||||
|
||||
@ -356,10 +302,11 @@ pub fn parseFromStub(
|
||||
lib_stub: LibStub,
|
||||
dylib_id: u16,
|
||||
dependent_libs: anytype,
|
||||
name: []const u8,
|
||||
) !void {
|
||||
if (lib_stub.inner.len == 0) return error.EmptyStubFile;
|
||||
|
||||
log.debug("parsing shared library from stub '{s}'", .{self.name});
|
||||
log.debug("parsing shared library from stub '{s}'", .{name});
|
||||
|
||||
const umbrella_lib = lib_stub.inner[0];
|
||||
|
||||
|
||||
@ -3,6 +3,7 @@ const Object = @This();
|
||||
const std = @import("std");
|
||||
const build_options = @import("build_options");
|
||||
const assert = std.debug.assert;
|
||||
const dwarf = std.dwarf;
|
||||
const fs = std.fs;
|
||||
const io = std.io;
|
||||
const log = std.log.scoped(.link);
|
||||
@ -14,43 +15,20 @@ const trace = @import("../../tracy.zig").trace;
|
||||
|
||||
const Allocator = mem.Allocator;
|
||||
const Atom = @import("Atom.zig");
|
||||
const LoadCommandIterator = macho.LoadCommandIterator;
|
||||
const MachO = @import("../MachO.zig");
|
||||
const MatchingSection = MachO.MatchingSection;
|
||||
const SymbolWithLoc = MachO.SymbolWithLoc;
|
||||
|
||||
file: fs.File,
|
||||
name: []const u8,
|
||||
mtime: u64,
|
||||
|
||||
/// Data contents of the file. Includes sections, and data of load commands.
|
||||
/// Excludes the backing memory for the header and load commands.
|
||||
/// Initialized in `parse`.
|
||||
contents: []const u8 = undefined,
|
||||
|
||||
file_offset: ?u32 = null,
|
||||
contents: []align(@alignOf(u64)) const u8,
|
||||
|
||||
header: macho.mach_header_64 = undefined,
|
||||
|
||||
load_commands: std.ArrayListUnmanaged(macho.LoadCommand) = .{},
|
||||
|
||||
segment_cmd_index: ?u16 = null,
|
||||
text_section_index: ?u16 = null,
|
||||
symtab_cmd_index: ?u16 = null,
|
||||
dysymtab_cmd_index: ?u16 = null,
|
||||
build_version_cmd_index: ?u16 = null,
|
||||
data_in_code_cmd_index: ?u16 = null,
|
||||
|
||||
// __DWARF segment sections
|
||||
dwarf_debug_info_index: ?u16 = null,
|
||||
dwarf_debug_abbrev_index: ?u16 = null,
|
||||
dwarf_debug_str_index: ?u16 = null,
|
||||
dwarf_debug_line_index: ?u16 = null,
|
||||
dwarf_debug_line_str_index: ?u16 = null,
|
||||
dwarf_debug_ranges_index: ?u16 = null,
|
||||
in_symtab: []const macho.nlist_64 = undefined,
|
||||
in_strtab: []const u8 = undefined,
|
||||
|
||||
symtab: std.ArrayListUnmanaged(macho.nlist_64) = .{},
|
||||
strtab: []const u8 = &.{},
|
||||
data_in_code_entries: []const macho.data_in_code_entry = &.{},
|
||||
sections: std.ArrayListUnmanaged(macho.section_64) = .{},
|
||||
|
||||
sections_as_symbols: std.AutoHashMapUnmanaged(u16, u32) = .{},
|
||||
|
||||
@ -61,12 +39,8 @@ managed_atoms: std.ArrayListUnmanaged(*Atom) = .{},
|
||||
atom_by_index_table: std.AutoHashMapUnmanaged(u32, *Atom) = .{},
|
||||
|
||||
pub fn deinit(self: *Object, gpa: Allocator) void {
|
||||
for (self.load_commands.items) |*lc| {
|
||||
lc.deinit(gpa);
|
||||
}
|
||||
self.load_commands.deinit(gpa);
|
||||
gpa.free(self.contents);
|
||||
self.symtab.deinit(gpa);
|
||||
self.sections.deinit(gpa);
|
||||
self.sections_as_symbols.deinit(gpa);
|
||||
self.atom_by_index_table.deinit(gpa);
|
||||
|
||||
@ -77,22 +51,15 @@ pub fn deinit(self: *Object, gpa: Allocator) void {
|
||||
self.managed_atoms.deinit(gpa);
|
||||
|
||||
gpa.free(self.name);
|
||||
gpa.free(self.contents);
|
||||
}
|
||||
|
||||
pub fn parse(self: *Object, allocator: Allocator, cpu_arch: std.Target.Cpu.Arch) !void {
|
||||
const file_stat = try self.file.stat();
|
||||
const file_size = math.cast(usize, file_stat.size) orelse return error.Overflow;
|
||||
self.contents = try self.file.readToEndAlloc(allocator, file_size);
|
||||
|
||||
var stream = std.io.fixedBufferStream(self.contents);
|
||||
const reader = stream.reader();
|
||||
|
||||
const file_offset = self.file_offset orelse 0;
|
||||
if (file_offset > 0) {
|
||||
try reader.context.seekTo(file_offset);
|
||||
}
|
||||
|
||||
self.header = try reader.readStruct(macho.mach_header_64);
|
||||
|
||||
if (self.header.filetype != macho.MH_OBJECT) {
|
||||
log.debug("invalid filetype: expected 0x{x}, found 0x{x}", .{
|
||||
macho.MH_OBJECT,
|
||||
@ -110,92 +77,54 @@ pub fn parse(self: *Object, allocator: Allocator, cpu_arch: std.Target.Cpu.Arch)
|
||||
},
|
||||
};
|
||||
if (this_arch != cpu_arch) {
|
||||
log.err("mismatched cpu architecture: expected {}, found {}", .{ cpu_arch, this_arch });
|
||||
log.err("mismatched cpu architecture: expected {s}, found {s}", .{
|
||||
@tagName(cpu_arch),
|
||||
@tagName(this_arch),
|
||||
});
|
||||
return error.MismatchedCpuArchitecture;
|
||||
}
|
||||
|
||||
try self.load_commands.ensureUnusedCapacity(allocator, self.header.ncmds);
|
||||
|
||||
var i: u16 = 0;
|
||||
while (i < self.header.ncmds) : (i += 1) {
|
||||
var cmd = try macho.LoadCommand.read(allocator, reader);
|
||||
var it = LoadCommandIterator{
|
||||
.ncmds = self.header.ncmds,
|
||||
.buffer = self.contents[@sizeOf(macho.mach_header_64)..][0..self.header.sizeofcmds],
|
||||
};
|
||||
while (it.next()) |cmd| {
|
||||
switch (cmd.cmd()) {
|
||||
.SEGMENT_64 => {
|
||||
self.segment_cmd_index = i;
|
||||
var seg = cmd.segment;
|
||||
for (seg.sections.items) |*sect, j| {
|
||||
const index = @intCast(u16, j);
|
||||
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;
|
||||
} else if (mem.eql(u8, sectname, "__debug_abbrev")) {
|
||||
self.dwarf_debug_abbrev_index = index;
|
||||
} else if (mem.eql(u8, sectname, "__debug_str")) {
|
||||
self.dwarf_debug_str_index = index;
|
||||
} else if (mem.eql(u8, sectname, "__debug_line")) {
|
||||
self.dwarf_debug_line_index = index;
|
||||
} else if (mem.eql(u8, sectname, "__debug_line_str")) {
|
||||
self.dwarf_debug_line_str_index = index;
|
||||
} else if (mem.eql(u8, sectname, "__debug_ranges")) {
|
||||
self.dwarf_debug_ranges_index = index;
|
||||
}
|
||||
} else if (mem.eql(u8, segname, "__TEXT")) {
|
||||
if (mem.eql(u8, sectname, "__text")) {
|
||||
self.text_section_index = index;
|
||||
}
|
||||
}
|
||||
|
||||
sect.offset += file_offset;
|
||||
if (sect.reloff > 0) {
|
||||
sect.reloff += file_offset;
|
||||
}
|
||||
const segment = cmd.cast(macho.segment_command_64).?;
|
||||
try self.sections.ensureUnusedCapacity(allocator, segment.nsects);
|
||||
for (cmd.getSections()) |sect| {
|
||||
self.sections.appendAssumeCapacity(sect);
|
||||
}
|
||||
|
||||
seg.inner.fileoff += file_offset;
|
||||
},
|
||||
.SYMTAB => {
|
||||
self.symtab_cmd_index = i;
|
||||
cmd.symtab.symoff += file_offset;
|
||||
cmd.symtab.stroff += file_offset;
|
||||
},
|
||||
.DYSYMTAB => {
|
||||
self.dysymtab_cmd_index = i;
|
||||
},
|
||||
.BUILD_VERSION => {
|
||||
self.build_version_cmd_index = i;
|
||||
},
|
||||
.DATA_IN_CODE => {
|
||||
self.data_in_code_cmd_index = i;
|
||||
cmd.linkedit_data.dataoff += file_offset;
|
||||
},
|
||||
else => {
|
||||
log.debug("Unknown load command detected: 0x{x}.", .{@enumToInt(cmd.cmd())});
|
||||
const symtab = cmd.cast(macho.symtab_command).?;
|
||||
self.in_symtab = @ptrCast(
|
||||
[*]const macho.nlist_64,
|
||||
@alignCast(@alignOf(macho.nlist_64), &self.contents[symtab.symoff]),
|
||||
)[0..symtab.nsyms];
|
||||
self.in_strtab = self.contents[symtab.stroff..][0..symtab.strsize];
|
||||
try self.symtab.appendSlice(allocator, self.in_symtab);
|
||||
},
|
||||
else => {},
|
||||
}
|
||||
self.load_commands.appendAssumeCapacity(cmd);
|
||||
}
|
||||
|
||||
try self.parseSymtab(allocator);
|
||||
}
|
||||
|
||||
const Context = struct {
|
||||
symtab: []const macho.nlist_64,
|
||||
strtab: []const u8,
|
||||
object: *const Object,
|
||||
};
|
||||
|
||||
const SymbolAtIndex = struct {
|
||||
index: u32,
|
||||
|
||||
fn getSymbol(self: SymbolAtIndex, ctx: Context) macho.nlist_64 {
|
||||
return ctx.symtab[self.index];
|
||||
return ctx.object.getSourceSymbol(self.index).?;
|
||||
}
|
||||
|
||||
fn getSymbolName(self: SymbolAtIndex, ctx: Context) []const u8 {
|
||||
const sym = self.getSymbol(ctx);
|
||||
assert(sym.n_strx < ctx.strtab.len);
|
||||
return mem.sliceTo(@ptrCast([*:0]const u8, ctx.strtab.ptr + sym.n_strx), 0);
|
||||
return ctx.object.getString(sym.n_strx);
|
||||
}
|
||||
|
||||
/// Returns whether lhs is less than rhs by allocated address in object file.
|
||||
@ -293,7 +222,6 @@ pub fn splitIntoAtomsOneShot(self: *Object, macho_file: *MachO, object_id: u32)
|
||||
defer tracy.end();
|
||||
|
||||
const gpa = macho_file.base.allocator;
|
||||
const seg = self.load_commands.items[self.segment_cmd_index.?].segment;
|
||||
|
||||
log.debug("splitting object({d}, {s}) into atoms: one-shot mode", .{ object_id, self.name });
|
||||
|
||||
@ -302,13 +230,12 @@ pub fn splitIntoAtomsOneShot(self: *Object, macho_file: *MachO, object_id: u32)
|
||||
// the GO compiler does not necessarily respect that therefore we sort immediately by type
|
||||
// and address within.
|
||||
const context = Context{
|
||||
.symtab = self.getSourceSymtab(),
|
||||
.strtab = self.strtab,
|
||||
.object = self,
|
||||
};
|
||||
var sorted_all_syms = try std.ArrayList(SymbolAtIndex).initCapacity(gpa, context.symtab.len);
|
||||
var sorted_all_syms = try std.ArrayList(SymbolAtIndex).initCapacity(gpa, self.in_symtab.len);
|
||||
defer sorted_all_syms.deinit();
|
||||
|
||||
for (context.symtab) |_, index| {
|
||||
for (self.in_symtab) |_, index| {
|
||||
sorted_all_syms.appendAssumeCapacity(.{ .index = @intCast(u32, index) });
|
||||
}
|
||||
|
||||
@ -320,36 +247,36 @@ pub fn splitIntoAtomsOneShot(self: *Object, macho_file: *MachO, object_id: u32)
|
||||
|
||||
// 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 iundefsym = blk: {
|
||||
const dysymtab = self.parseDysymtab() orelse {
|
||||
var iundefsym: usize = sorted_all_syms.items.len;
|
||||
while (iundefsym > 0) : (iundefsym -= 1) {
|
||||
const sym = sorted_all_syms.items[iundefsym - 1].getSymbol(context);
|
||||
if (sym.sect()) break;
|
||||
}
|
||||
break :blk iundefsym;
|
||||
};
|
||||
break :blk dysymtab.iundefsym;
|
||||
} else blk: {
|
||||
var iundefsym: usize = sorted_all_syms.items.len;
|
||||
while (iundefsym > 0) : (iundefsym -= 1) {
|
||||
const sym = sorted_all_syms.items[iundefsym - 1].getSymbol(context);
|
||||
if (sym.sect()) break;
|
||||
}
|
||||
break :blk iundefsym;
|
||||
};
|
||||
|
||||
// We only care about defined symbols, so filter every other out.
|
||||
const sorted_syms = sorted_all_syms.items[0..iundefsym];
|
||||
const subsections_via_symbols = self.header.flags & macho.MH_SUBSECTIONS_VIA_SYMBOLS != 0;
|
||||
|
||||
for (seg.sections.items) |sect, id| {
|
||||
for (self.sections.items) |sect, id| {
|
||||
const sect_id = @intCast(u8, id);
|
||||
log.debug("splitting section '{s},{s}' into atoms", .{ sect.segName(), sect.sectName() });
|
||||
|
||||
// Get matching segment/section in the final artifact.
|
||||
const match = (try macho_file.getMatchingSection(sect)) orelse {
|
||||
const match = (try macho_file.getOutputSection(sect)) orelse {
|
||||
log.debug(" unhandled section", .{});
|
||||
continue;
|
||||
};
|
||||
|
||||
log.debug(" output sect({d}, '{s},{s}')", .{
|
||||
macho_file.getSectionOrdinal(match),
|
||||
macho_file.getSection(match).segName(),
|
||||
macho_file.getSection(match).sectName(),
|
||||
match + 1,
|
||||
macho_file.sections.items(.header)[match].segName(),
|
||||
macho_file.sections.items(.header)[match].sectName(),
|
||||
});
|
||||
|
||||
const cpu_arch = macho_file.base.options.target.cpu.arch;
|
||||
@ -359,14 +286,13 @@ pub fn splitIntoAtomsOneShot(self: *Object, macho_file: *MachO, object_id: u32)
|
||||
};
|
||||
|
||||
// Read section's code
|
||||
const code: ?[]const u8 = if (!is_zerofill) try self.getSectionContents(sect_id) else null;
|
||||
const code: ?[]const u8 = if (!is_zerofill) try self.getSectionContents(sect) else null;
|
||||
|
||||
// Read section's list of relocations
|
||||
const raw_relocs = self.contents[sect.reloff..][0 .. sect.nreloc * @sizeOf(macho.relocation_info)];
|
||||
const relocs = mem.bytesAsSlice(
|
||||
macho.relocation_info,
|
||||
@alignCast(@alignOf(macho.relocation_info), raw_relocs),
|
||||
);
|
||||
const relocs = @ptrCast(
|
||||
[*]const macho.relocation_info,
|
||||
@alignCast(@alignOf(macho.relocation_info), &self.contents[sect.reloff]),
|
||||
)[0..sect.nreloc];
|
||||
|
||||
// Symbols within this section only.
|
||||
const filtered_syms = filterSymbolsByAddress(
|
||||
@ -387,7 +313,7 @@ pub fn splitIntoAtomsOneShot(self: *Object, macho_file: *MachO, object_id: u32)
|
||||
try self.symtab.append(gpa, .{
|
||||
.n_strx = 0,
|
||||
.n_type = macho.N_SECT,
|
||||
.n_sect = macho_file.getSectionOrdinal(match),
|
||||
.n_sect = match + 1,
|
||||
.n_desc = 0,
|
||||
.n_value = sect.addr,
|
||||
});
|
||||
@ -476,7 +402,7 @@ pub fn splitIntoAtomsOneShot(self: *Object, macho_file: *MachO, object_id: u32)
|
||||
try self.symtab.append(gpa, .{
|
||||
.n_strx = 0,
|
||||
.n_type = macho.N_SECT,
|
||||
.n_sect = macho_file.getSectionOrdinal(match),
|
||||
.n_sect = match + 1,
|
||||
.n_desc = 0,
|
||||
.n_value = addr,
|
||||
});
|
||||
@ -501,7 +427,7 @@ pub fn splitIntoAtomsOneShot(self: *Object, macho_file: *MachO, object_id: u32)
|
||||
try self.symtab.append(gpa, .{
|
||||
.n_strx = 0,
|
||||
.n_type = macho.N_SECT,
|
||||
.n_sect = macho_file.getSectionOrdinal(match),
|
||||
.n_sect = match + 1,
|
||||
.n_desc = 0,
|
||||
.n_value = sect.addr,
|
||||
});
|
||||
@ -535,21 +461,21 @@ fn createAtomFromSubsection(
|
||||
code: ?[]const u8,
|
||||
relocs: []const macho.relocation_info,
|
||||
indexes: []const SymbolAtIndex,
|
||||
match: MatchingSection,
|
||||
match: u8,
|
||||
sect: macho.section_64,
|
||||
) !*Atom {
|
||||
const gpa = macho_file.base.allocator;
|
||||
const sym = self.symtab.items[sym_index];
|
||||
const atom = try MachO.createEmptyAtom(gpa, sym_index, size, alignment);
|
||||
atom.file = object_id;
|
||||
self.symtab.items[sym_index].n_sect = macho_file.getSectionOrdinal(match);
|
||||
self.symtab.items[sym_index].n_sect = match + 1;
|
||||
|
||||
log.debug("creating ATOM(%{d}, '{s}') in sect({d}, '{s},{s}') in object({d})", .{
|
||||
sym_index,
|
||||
self.getString(sym.n_strx),
|
||||
macho_file.getSectionOrdinal(match),
|
||||
macho_file.getSection(match).segName(),
|
||||
macho_file.getSection(match).sectName(),
|
||||
match + 1,
|
||||
macho_file.sections.items(.header)[match].segName(),
|
||||
macho_file.sections.items(.header)[match].sectName(),
|
||||
object_id,
|
||||
});
|
||||
|
||||
@ -577,7 +503,7 @@ fn createAtomFromSubsection(
|
||||
try atom.contained.ensureTotalCapacity(gpa, indexes.len);
|
||||
for (indexes) |inner_sym_index| {
|
||||
const inner_sym = &self.symtab.items[inner_sym_index.index];
|
||||
inner_sym.n_sect = macho_file.getSectionOrdinal(match);
|
||||
inner_sym.n_sect = match + 1;
|
||||
atom.contained.appendAssumeCapacity(.{
|
||||
.sym_index = inner_sym_index.index,
|
||||
.offset = inner_sym.n_value - sym.n_value,
|
||||
@ -589,48 +515,84 @@ fn createAtomFromSubsection(
|
||||
return atom;
|
||||
}
|
||||
|
||||
fn parseSymtab(self: *Object, allocator: Allocator) !void {
|
||||
const index = self.symtab_cmd_index orelse return;
|
||||
const symtab = self.load_commands.items[index].symtab;
|
||||
try self.symtab.appendSlice(allocator, self.getSourceSymtab());
|
||||
self.strtab = self.contents[symtab.stroff..][0..symtab.strsize];
|
||||
}
|
||||
|
||||
pub fn getSourceSymtab(self: Object) []const macho.nlist_64 {
|
||||
const index = self.symtab_cmd_index orelse return &[0]macho.nlist_64{};
|
||||
const symtab = self.load_commands.items[index].symtab;
|
||||
const symtab_size = @sizeOf(macho.nlist_64) * symtab.nsyms;
|
||||
const raw_symtab = self.contents[symtab.symoff..][0..symtab_size];
|
||||
return mem.bytesAsSlice(
|
||||
macho.nlist_64,
|
||||
@alignCast(@alignOf(macho.nlist_64), raw_symtab),
|
||||
);
|
||||
}
|
||||
|
||||
pub fn getSourceSymbol(self: Object, index: u32) ?macho.nlist_64 {
|
||||
const symtab = self.getSourceSymtab();
|
||||
if (index >= symtab.len) return null;
|
||||
return symtab[index];
|
||||
if (index >= self.in_symtab.len) return null;
|
||||
return self.in_symtab[index];
|
||||
}
|
||||
|
||||
pub fn getSourceSection(self: Object, index: u16) macho.section_64 {
|
||||
const seg = self.load_commands.items[self.segment_cmd_index.?].segment;
|
||||
assert(index < seg.sections.items.len);
|
||||
return seg.sections.items[index];
|
||||
assert(index < self.sections.items.len);
|
||||
return self.sections.items[index];
|
||||
}
|
||||
|
||||
pub fn parseDataInCode(self: Object) ?[]const macho.data_in_code_entry {
|
||||
const index = self.data_in_code_cmd_index orelse return null;
|
||||
const data_in_code = self.load_commands.items[index].linkedit_data;
|
||||
const raw_dice = self.contents[data_in_code.dataoff..][0..data_in_code.datasize];
|
||||
return mem.bytesAsSlice(
|
||||
macho.data_in_code_entry,
|
||||
@alignCast(@alignOf(macho.data_in_code_entry), raw_dice),
|
||||
);
|
||||
var it = LoadCommandIterator{
|
||||
.ncmds = self.header.ncmds,
|
||||
.buffer = self.contents[@sizeOf(macho.mach_header_64)..][0..self.header.sizeofcmds],
|
||||
};
|
||||
while (it.next()) |cmd| {
|
||||
switch (cmd.cmd()) {
|
||||
.DATA_IN_CODE => {
|
||||
const dice = cmd.cast(macho.linkedit_data_command).?;
|
||||
const ndice = @divExact(dice.datasize, @sizeOf(macho.data_in_code_entry));
|
||||
return @ptrCast(
|
||||
[*]const macho.data_in_code_entry,
|
||||
@alignCast(@alignOf(macho.data_in_code_entry), &self.contents[dice.dataoff]),
|
||||
)[0..ndice];
|
||||
},
|
||||
else => {},
|
||||
}
|
||||
} else return null;
|
||||
}
|
||||
|
||||
pub fn getSectionContents(self: Object, index: u16) error{Overflow}![]const u8 {
|
||||
const sect = self.getSourceSection(index);
|
||||
fn parseDysymtab(self: Object) ?macho.dysymtab_command {
|
||||
var it = LoadCommandIterator{
|
||||
.ncmds = self.header.ncmds,
|
||||
.buffer = self.contents[@sizeOf(macho.mach_header_64)..][0..self.header.sizeofcmds],
|
||||
};
|
||||
while (it.next()) |cmd| {
|
||||
switch (cmd.cmd()) {
|
||||
.DYSYMTAB => {
|
||||
return cmd.cast(macho.dysymtab_command).?;
|
||||
},
|
||||
else => {},
|
||||
}
|
||||
} else return null;
|
||||
}
|
||||
|
||||
pub fn parseDwarfInfo(self: Object) error{Overflow}!dwarf.DwarfInfo {
|
||||
var di = dwarf.DwarfInfo{
|
||||
.endian = .Little,
|
||||
.debug_info = &[0]u8{},
|
||||
.debug_abbrev = &[0]u8{},
|
||||
.debug_str = &[0]u8{},
|
||||
.debug_line = &[0]u8{},
|
||||
.debug_line_str = &[0]u8{},
|
||||
.debug_ranges = &[0]u8{},
|
||||
};
|
||||
for (self.sections.items) |sect| {
|
||||
const segname = sect.segName();
|
||||
const sectname = sect.sectName();
|
||||
if (mem.eql(u8, segname, "__DWARF")) {
|
||||
if (mem.eql(u8, sectname, "__debug_info")) {
|
||||
di.debug_info = try self.getSectionContents(sect);
|
||||
} else if (mem.eql(u8, sectname, "__debug_abbrev")) {
|
||||
di.debug_abbrev = try self.getSectionContents(sect);
|
||||
} else if (mem.eql(u8, sectname, "__debug_str")) {
|
||||
di.debug_str = try self.getSectionContents(sect);
|
||||
} else if (mem.eql(u8, sectname, "__debug_line")) {
|
||||
di.debug_line = try self.getSectionContents(sect);
|
||||
} else if (mem.eql(u8, sectname, "__debug_line_str")) {
|
||||
di.debug_line_str = try self.getSectionContents(sect);
|
||||
} else if (mem.eql(u8, sectname, "__debug_ranges")) {
|
||||
di.debug_ranges = try self.getSectionContents(sect);
|
||||
}
|
||||
}
|
||||
}
|
||||
return di;
|
||||
}
|
||||
|
||||
pub fn getSectionContents(self: Object, sect: macho.section_64) error{Overflow}![]const u8 {
|
||||
const size = math.cast(usize, sect.size) orelse return error.Overflow;
|
||||
log.debug("getting {s},{s} data at 0x{x} - 0x{x}", .{
|
||||
sect.segName(),
|
||||
@ -642,8 +604,8 @@ pub fn getSectionContents(self: Object, index: u16) error{Overflow}![]const u8 {
|
||||
}
|
||||
|
||||
pub fn getString(self: Object, off: u32) []const u8 {
|
||||
assert(off < self.strtab.len);
|
||||
return mem.sliceTo(@ptrCast([*:0]const u8, self.strtab.ptr + off), 0);
|
||||
assert(off < self.in_strtab.len);
|
||||
return mem.sliceTo(@ptrCast([*:0]const u8, self.in_strtab.ptr + off), 0);
|
||||
}
|
||||
|
||||
pub fn getAtomForSymbol(self: Object, sym_index: u32) ?*Atom {
|
||||
|
||||
@ -8,7 +8,6 @@ const mem = std.mem;
|
||||
const Allocator = mem.Allocator;
|
||||
const Atom = @import("Atom.zig");
|
||||
const MachO = @import("../MachO.zig");
|
||||
const MatchingSection = MachO.MatchingSection;
|
||||
|
||||
pub fn gcAtoms(macho_file: *MachO) !void {
|
||||
const gpa = macho_file.base.allocator;
|
||||
@ -25,12 +24,12 @@ pub fn gcAtoms(macho_file: *MachO) !void {
|
||||
try prune(arena, alive, macho_file);
|
||||
}
|
||||
|
||||
fn removeAtomFromSection(atom: *Atom, match: MatchingSection, macho_file: *MachO) void {
|
||||
const sect = macho_file.getSectionPtr(match);
|
||||
fn removeAtomFromSection(atom: *Atom, match: u8, macho_file: *MachO) void {
|
||||
var section = macho_file.sections.get(match);
|
||||
|
||||
// If we want to enable GC for incremental codepath, we need to take into
|
||||
// account any padding that might have been left here.
|
||||
sect.size -= atom.size;
|
||||
section.header.size -= atom.size;
|
||||
|
||||
if (atom.prev) |prev| {
|
||||
prev.next = atom.next;
|
||||
@ -38,15 +37,16 @@ fn removeAtomFromSection(atom: *Atom, match: MatchingSection, macho_file: *MachO
|
||||
if (atom.next) |next| {
|
||||
next.prev = atom.prev;
|
||||
} else {
|
||||
const last = macho_file.atoms.getPtr(match).?;
|
||||
if (atom.prev) |prev| {
|
||||
last.* = prev;
|
||||
section.last_atom = prev;
|
||||
} else {
|
||||
// The section will be GCed in the next step.
|
||||
last.* = undefined;
|
||||
sect.size = 0;
|
||||
section.last_atom = null;
|
||||
section.header.size = 0;
|
||||
}
|
||||
}
|
||||
|
||||
macho_file.sections.set(match, section);
|
||||
}
|
||||
|
||||
fn collectRoots(roots: *std.AutoHashMap(*Atom, void), macho_file: *MachO) !void {
|
||||
@ -173,19 +173,19 @@ fn mark(
|
||||
fn prune(arena: Allocator, alive: std.AutoHashMap(*Atom, void), macho_file: *MachO) !void {
|
||||
// Any section that ends up here will be updated, that is,
|
||||
// its size and alignment recalculated.
|
||||
var gc_sections = std.AutoHashMap(MatchingSection, void).init(arena);
|
||||
var gc_sections = std.AutoHashMap(u8, void).init(arena);
|
||||
var loop: bool = true;
|
||||
while (loop) {
|
||||
loop = false;
|
||||
|
||||
for (macho_file.objects.items) |object| {
|
||||
for (object.getSourceSymtab()) |_, source_index| {
|
||||
for (object.in_symtab) |_, source_index| {
|
||||
const atom = object.getAtomForSymbol(@intCast(u32, source_index)) orelse continue;
|
||||
if (alive.contains(atom)) continue;
|
||||
|
||||
const global = atom.getSymbolWithLoc();
|
||||
const sym = atom.getSymbolPtr(macho_file);
|
||||
const match = macho_file.getMatchingSectionFromOrdinal(sym.n_sect);
|
||||
const match = sym.n_sect - 1;
|
||||
|
||||
if (sym.n_desc == MachO.N_DESC_GCED) continue;
|
||||
if (!sym.ext() and !refersDead(atom, macho_file)) continue;
|
||||
@ -232,7 +232,7 @@ fn prune(arena: Allocator, alive: std.AutoHashMap(*Atom, void), macho_file: *Mac
|
||||
|
||||
// TODO tombstone
|
||||
const atom = entry.getAtom(macho_file);
|
||||
const match = macho_file.getMatchingSectionFromOrdinal(sym.n_sect);
|
||||
const match = sym.n_sect - 1;
|
||||
removeAtomFromSection(atom, match, macho_file);
|
||||
_ = try gc_sections.put(match, {});
|
||||
_ = macho_file.got_entries_table.remove(entry.target);
|
||||
@ -244,7 +244,7 @@ fn prune(arena: Allocator, alive: std.AutoHashMap(*Atom, void), macho_file: *Mac
|
||||
|
||||
// TODO tombstone
|
||||
const atom = entry.getAtom(macho_file);
|
||||
const match = macho_file.getMatchingSectionFromOrdinal(sym.n_sect);
|
||||
const match = sym.n_sect - 1;
|
||||
removeAtomFromSection(atom, match, macho_file);
|
||||
_ = try gc_sections.put(match, {});
|
||||
_ = macho_file.stubs_table.remove(entry.target);
|
||||
@ -256,7 +256,7 @@ fn prune(arena: Allocator, alive: std.AutoHashMap(*Atom, void), macho_file: *Mac
|
||||
|
||||
// TODO tombstone
|
||||
const atom = entry.getAtom(macho_file);
|
||||
const match = macho_file.getMatchingSectionFromOrdinal(sym.n_sect);
|
||||
const match = sym.n_sect - 1;
|
||||
removeAtomFromSection(atom, match, macho_file);
|
||||
_ = try gc_sections.put(match, {});
|
||||
_ = macho_file.tlv_ptr_entries_table.remove(entry.target);
|
||||
@ -265,13 +265,13 @@ fn prune(arena: Allocator, alive: std.AutoHashMap(*Atom, void), macho_file: *Mac
|
||||
var gc_sections_it = gc_sections.iterator();
|
||||
while (gc_sections_it.next()) |entry| {
|
||||
const match = entry.key_ptr.*;
|
||||
const sect = macho_file.getSectionPtr(match);
|
||||
if (sect.size == 0) continue; // Pruning happens automatically in next step.
|
||||
var section = macho_file.sections.get(match);
|
||||
if (section.header.size == 0) continue; // Pruning happens automatically in next step.
|
||||
|
||||
sect.@"align" = 0;
|
||||
sect.size = 0;
|
||||
section.header.@"align" = 0;
|
||||
section.header.size = 0;
|
||||
|
||||
var atom = macho_file.atoms.get(match).?;
|
||||
var atom = section.last_atom.?;
|
||||
|
||||
while (atom.prev) |prev| {
|
||||
atom = prev;
|
||||
@ -279,14 +279,16 @@ fn prune(arena: Allocator, alive: std.AutoHashMap(*Atom, void), macho_file: *Mac
|
||||
|
||||
while (true) {
|
||||
const atom_alignment = try math.powi(u32, 2, atom.alignment);
|
||||
const aligned_end_addr = mem.alignForwardGeneric(u64, sect.size, atom_alignment);
|
||||
const padding = aligned_end_addr - sect.size;
|
||||
sect.size += padding + atom.size;
|
||||
sect.@"align" = @maximum(sect.@"align", atom.alignment);
|
||||
const aligned_end_addr = mem.alignForwardGeneric(u64, section.header.size, atom_alignment);
|
||||
const padding = aligned_end_addr - section.header.size;
|
||||
section.header.size += padding + atom.size;
|
||||
section.header.@"align" = @maximum(section.header.@"align", atom.alignment);
|
||||
|
||||
if (atom.next) |next| {
|
||||
atom = next;
|
||||
} else break;
|
||||
}
|
||||
|
||||
macho_file.sections.set(match, section);
|
||||
}
|
||||
}
|
||||
|
||||
@ -46,7 +46,9 @@ pub fn getLibraryOffset(reader: anytype, cpu_arch: std.Target.Cpu.Arch) !u64 {
|
||||
return fat_arch.offset;
|
||||
}
|
||||
} else {
|
||||
log.err("Could not find matching cpu architecture in fat library: expected {}", .{cpu_arch});
|
||||
log.err("Could not find matching cpu architecture in fat library: expected {s}", .{
|
||||
@tagName(cpu_arch),
|
||||
});
|
||||
return error.MismatchedCpuArchitecture;
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user