MachO: update to new std.io APIs

This commit is contained in:
Andrew Kelley 2025-07-01 18:14:45 -07:00
parent f71d97e4cb
commit c8fcd2ff2c
25 changed files with 1591 additions and 1660 deletions

View File

@ -444,7 +444,7 @@ pub fn renderMessage(comp: *Compilation, m: anytype, msg: Message) void {
printRt(m, prop.msg, .{"{s}"}, .{&str}); printRt(m, prop.msg, .{"{s}"}, .{&str});
} else { } else {
var buf: [3]u8 = undefined; var buf: [3]u8 = undefined;
const str = std.fmt.bufPrint(&buf, "x{x}", .{&.{msg.extra.invalid_escape.char}}) catch unreachable; const str = std.fmt.bufPrint(&buf, "x{x}", .{msg.extra.invalid_escape.char}) catch unreachable;
printRt(m, prop.msg, .{"{s}"}, .{str}); printRt(m, prop.msg, .{"{s}"}, .{str});
} }
}, },
@ -525,13 +525,13 @@ fn tagKind(d: *Diagnostics, tag: Tag, langopts: LangOpts) Kind {
} }
const MsgWriter = struct { const MsgWriter = struct {
w: std.io.BufferedWriter(4096, std.fs.File.Writer), w: *std.fs.File.Writer,
config: std.io.tty.Config, config: std.io.tty.Config,
fn init(config: std.io.tty.Config) MsgWriter { fn init(config: std.io.tty.Config, buffer: []u8) MsgWriter {
std.debug.lockStdErr(); std.debug.lockStdErr();
return .{ return .{
.w = std.io.bufferedWriter(std.fs.File.stderr().deprecatedWriter()), .w = std.fs.stderr().writer(buffer),
.config = config, .config = config,
}; };
} }

431
src/deprecated.zig Normal file
View File

@ -0,0 +1,431 @@
//! Deprecated. Stop using this API
const std = @import("std");
const math = std.math;
const mem = std.mem;
const Allocator = mem.Allocator;
const assert = std.debug.assert;
const testing = std.testing;
pub fn LinearFifo(comptime T: type) type {
return struct {
allocator: Allocator,
buf: []T,
head: usize,
count: usize,
const Self = @This();
pub fn init(allocator: Allocator) Self {
return .{
.allocator = allocator,
.buf = &.{},
.head = 0,
.count = 0,
};
}
pub fn deinit(self: *Self) void {
self.allocator.free(self.buf);
self.* = undefined;
}
pub fn realign(self: *Self) void {
if (self.buf.len - self.head >= self.count) {
mem.copyForwards(T, self.buf[0..self.count], self.buf[self.head..][0..self.count]);
self.head = 0;
} else {
var tmp: [4096 / 2 / @sizeOf(T)]T = undefined;
while (self.head != 0) {
const n = @min(self.head, tmp.len);
const m = self.buf.len - n;
@memcpy(tmp[0..n], self.buf[0..n]);
mem.copyForwards(T, self.buf[0..m], self.buf[n..][0..m]);
@memcpy(self.buf[m..][0..n], tmp[0..n]);
self.head -= n;
}
}
{ // set unused area to undefined
const unused = mem.sliceAsBytes(self.buf[self.count..]);
@memset(unused, undefined);
}
}
/// Reduce allocated capacity to `size`.
pub fn shrink(self: *Self, size: usize) void {
assert(size >= self.count);
self.realign();
self.buf = self.allocator.realloc(self.buf, size) catch |e| switch (e) {
error.OutOfMemory => return, // no problem, capacity is still correct then.
};
}
/// Ensure that the buffer can fit at least `size` items
pub fn ensureTotalCapacity(self: *Self, size: usize) !void {
if (self.buf.len >= size) return;
self.realign();
const new_size = math.ceilPowerOfTwo(usize, size) catch return error.OutOfMemory;
self.buf = try self.allocator.realloc(self.buf, new_size);
}
/// Makes sure at least `size` items are unused
pub fn ensureUnusedCapacity(self: *Self, size: usize) error{OutOfMemory}!void {
if (self.writableLength() >= size) return;
return try self.ensureTotalCapacity(math.add(usize, self.count, size) catch return error.OutOfMemory);
}
/// Returns number of items currently in fifo
pub fn readableLength(self: Self) usize {
return self.count;
}
/// Returns a writable slice from the 'read' end of the fifo
fn readableSliceMut(self: Self, offset: usize) []T {
if (offset > self.count) return &[_]T{};
var start = self.head + offset;
if (start >= self.buf.len) {
start -= self.buf.len;
return self.buf[start .. start + (self.count - offset)];
} else {
const end = @min(self.head + self.count, self.buf.len);
return self.buf[start..end];
}
}
/// Returns a readable slice from `offset`
pub fn readableSlice(self: Self, offset: usize) []const T {
return self.readableSliceMut(offset);
}
pub fn readableSliceOfLen(self: *Self, len: usize) []const T {
assert(len <= self.count);
const buf = self.readableSlice(0);
if (buf.len >= len) {
return buf[0..len];
} else {
self.realign();
return self.readableSlice(0)[0..len];
}
}
/// Discard first `count` items in the fifo
pub fn discard(self: *Self, count: usize) void {
assert(count <= self.count);
{ // set old range to undefined. Note: may be wrapped around
const slice = self.readableSliceMut(0);
if (slice.len >= count) {
const unused = mem.sliceAsBytes(slice[0..count]);
@memset(unused, undefined);
} else {
const unused = mem.sliceAsBytes(slice[0..]);
@memset(unused, undefined);
const unused2 = mem.sliceAsBytes(self.readableSliceMut(slice.len)[0 .. count - slice.len]);
@memset(unused2, undefined);
}
}
var head = self.head + count;
// Note it is safe to do a wrapping subtract as
// bitwise & with all 1s is a noop
head &= self.buf.len -% 1;
self.head = head;
self.count -= count;
}
/// Read the next item from the fifo
pub fn readItem(self: *Self) ?T {
if (self.count == 0) return null;
const c = self.buf[self.head];
self.discard(1);
return c;
}
/// Read data from the fifo into `dst`, returns number of items copied.
pub fn read(self: *Self, dst: []T) usize {
var dst_left = dst;
while (dst_left.len > 0) {
const slice = self.readableSlice(0);
if (slice.len == 0) break;
const n = @min(slice.len, dst_left.len);
@memcpy(dst_left[0..n], slice[0..n]);
self.discard(n);
dst_left = dst_left[n..];
}
return dst.len - dst_left.len;
}
/// Same as `read` except it returns an error union
/// The purpose of this function existing is to match `std.io.Reader` API.
fn readFn(self: *Self, dest: []u8) error{}!usize {
return self.read(dest);
}
/// Returns number of items available in fifo
pub fn writableLength(self: Self) usize {
return self.buf.len - self.count;
}
/// Returns the first section of writable buffer.
/// Note that this may be of length 0
pub fn writableSlice(self: Self, offset: usize) []T {
if (offset > self.buf.len) return &[_]T{};
const tail = self.head + offset + self.count;
if (tail < self.buf.len) {
return self.buf[tail..];
} else {
return self.buf[tail - self.buf.len ..][0 .. self.writableLength() - offset];
}
}
/// Returns a writable buffer of at least `size` items, allocating memory as needed.
/// Use `fifo.update` once you've written data to it.
pub fn writableWithSize(self: *Self, size: usize) ![]T {
try self.ensureUnusedCapacity(size);
// try to avoid realigning buffer
var slice = self.writableSlice(0);
if (slice.len < size) {
self.realign();
slice = self.writableSlice(0);
}
return slice;
}
/// Update the tail location of the buffer (usually follows use of writable/writableWithSize)
pub fn update(self: *Self, count: usize) void {
assert(self.count + count <= self.buf.len);
self.count += count;
}
/// Appends the data in `src` to the fifo.
/// You must have ensured there is enough space.
pub fn writeAssumeCapacity(self: *Self, src: []const T) void {
assert(self.writableLength() >= src.len);
var src_left = src;
while (src_left.len > 0) {
const writable_slice = self.writableSlice(0);
assert(writable_slice.len != 0);
const n = @min(writable_slice.len, src_left.len);
@memcpy(writable_slice[0..n], src_left[0..n]);
self.update(n);
src_left = src_left[n..];
}
}
/// Write a single item to the fifo
pub fn writeItem(self: *Self, item: T) !void {
try self.ensureUnusedCapacity(1);
return self.writeItemAssumeCapacity(item);
}
pub fn writeItemAssumeCapacity(self: *Self, item: T) void {
var tail = self.head + self.count;
tail &= self.buf.len - 1;
self.buf[tail] = item;
self.update(1);
}
/// Appends the data in `src` to the fifo.
/// Allocates more memory as necessary
pub fn write(self: *Self, src: []const T) !void {
try self.ensureUnusedCapacity(src.len);
return self.writeAssumeCapacity(src);
}
/// Same as `write` except it returns the number of bytes written, which is always the same
/// as `bytes.len`. The purpose of this function existing is to match `std.io.Writer` API.
fn appendWrite(self: *Self, bytes: []const u8) error{OutOfMemory}!usize {
try self.write(bytes);
return bytes.len;
}
/// Make `count` items available before the current read location
fn rewind(self: *Self, count: usize) void {
assert(self.writableLength() >= count);
var head = self.head + (self.buf.len - count);
head &= self.buf.len - 1;
self.head = head;
self.count += count;
}
/// Place data back into the read stream
pub fn unget(self: *Self, src: []const T) !void {
try self.ensureUnusedCapacity(src.len);
self.rewind(src.len);
const slice = self.readableSliceMut(0);
if (src.len < slice.len) {
@memcpy(slice[0..src.len], src);
} else {
@memcpy(slice, src[0..slice.len]);
const slice2 = self.readableSliceMut(slice.len);
@memcpy(slice2[0 .. src.len - slice.len], src[slice.len..]);
}
}
/// Returns the item at `offset`.
/// Asserts offset is within bounds.
pub fn peekItem(self: Self, offset: usize) T {
assert(offset < self.count);
var index = self.head + offset;
index &= self.buf.len - 1;
return self.buf[index];
}
pub fn toOwnedSlice(self: *Self) Allocator.Error![]T {
if (self.head != 0) self.realign();
assert(self.head == 0);
assert(self.count <= self.buf.len);
const allocator = self.allocator;
if (allocator.resize(self.buf, self.count)) {
const result = self.buf[0..self.count];
self.* = Self.init(allocator);
return result;
}
const new_memory = try allocator.dupe(T, self.buf[0..self.count]);
allocator.free(self.buf);
self.* = Self.init(allocator);
return new_memory;
}
};
}
test "LinearFifo(u8, .Dynamic) discard(0) from empty buffer should not error on overflow" {
var fifo = LinearFifo(u8, .Dynamic).init(testing.allocator);
defer fifo.deinit();
// If overflow is not explicitly allowed this will crash in debug / safe mode
fifo.discard(0);
}
test "LinearFifo(u8, .Dynamic)" {
var fifo = LinearFifo(u8, .Dynamic).init(testing.allocator);
defer fifo.deinit();
try fifo.write("HELLO");
try testing.expectEqual(@as(usize, 5), fifo.readableLength());
try testing.expectEqualSlices(u8, "HELLO", fifo.readableSlice(0));
{
var i: usize = 0;
while (i < 5) : (i += 1) {
try fifo.write(&[_]u8{fifo.peekItem(i)});
}
try testing.expectEqual(@as(usize, 10), fifo.readableLength());
try testing.expectEqualSlices(u8, "HELLOHELLO", fifo.readableSlice(0));
}
{
try testing.expectEqual(@as(u8, 'H'), fifo.readItem().?);
try testing.expectEqual(@as(u8, 'E'), fifo.readItem().?);
try testing.expectEqual(@as(u8, 'L'), fifo.readItem().?);
try testing.expectEqual(@as(u8, 'L'), fifo.readItem().?);
try testing.expectEqual(@as(u8, 'O'), fifo.readItem().?);
}
try testing.expectEqual(@as(usize, 5), fifo.readableLength());
{ // Writes that wrap around
try testing.expectEqual(@as(usize, 11), fifo.writableLength());
try testing.expectEqual(@as(usize, 6), fifo.writableSlice(0).len);
fifo.writeAssumeCapacity("6<chars<11");
try testing.expectEqualSlices(u8, "HELLO6<char", fifo.readableSlice(0));
try testing.expectEqualSlices(u8, "s<11", fifo.readableSlice(11));
try testing.expectEqualSlices(u8, "11", fifo.readableSlice(13));
try testing.expectEqualSlices(u8, "", fifo.readableSlice(15));
fifo.discard(11);
try testing.expectEqualSlices(u8, "s<11", fifo.readableSlice(0));
fifo.discard(4);
try testing.expectEqual(@as(usize, 0), fifo.readableLength());
}
{
const buf = try fifo.writableWithSize(12);
try testing.expectEqual(@as(usize, 12), buf.len);
var i: u8 = 0;
while (i < 10) : (i += 1) {
buf[i] = i + 'a';
}
fifo.update(10);
try testing.expectEqualSlices(u8, "abcdefghij", fifo.readableSlice(0));
}
{
try fifo.unget("prependedstring");
var result: [30]u8 = undefined;
try testing.expectEqualSlices(u8, "prependedstringabcdefghij", result[0..fifo.read(&result)]);
try fifo.unget("b");
try fifo.unget("a");
try testing.expectEqualSlices(u8, "ab", result[0..fifo.read(&result)]);
}
fifo.shrink(0);
{
try fifo.writer().print("{s}, {s}!", .{ "Hello", "World" });
var result: [30]u8 = undefined;
try testing.expectEqualSlices(u8, "Hello, World!", result[0..fifo.read(&result)]);
try testing.expectEqual(@as(usize, 0), fifo.readableLength());
}
{
try fifo.writer().writeAll("This is a test");
var result: [30]u8 = undefined;
try testing.expectEqualSlices(u8, "This", (try fifo.reader().readUntilDelimiterOrEof(&result, ' ')).?);
try testing.expectEqualSlices(u8, "is", (try fifo.reader().readUntilDelimiterOrEof(&result, ' ')).?);
try testing.expectEqualSlices(u8, "a", (try fifo.reader().readUntilDelimiterOrEof(&result, ' ')).?);
try testing.expectEqualSlices(u8, "test", (try fifo.reader().readUntilDelimiterOrEof(&result, ' ')).?);
}
{
try fifo.ensureTotalCapacity(1);
var in_fbs = std.io.fixedBufferStream("pump test");
var out_buf: [50]u8 = undefined;
var out_fbs = std.io.fixedBufferStream(&out_buf);
try fifo.pump(in_fbs.reader(), out_fbs.writer());
try testing.expectEqualSlices(u8, in_fbs.buffer, out_fbs.getWritten());
}
}
test LinearFifo {
inline for ([_]type{ u1, u8, u16, u64 }) |T| {
const FifoType = LinearFifo(T);
var fifo: FifoType = .init(testing.allocator);
defer fifo.deinit();
try fifo.write(&[_]T{ 0, 1, 1, 0, 1 });
try testing.expectEqual(@as(usize, 5), fifo.readableLength());
{
try testing.expectEqual(@as(T, 0), fifo.readItem().?);
try testing.expectEqual(@as(T, 1), fifo.readItem().?);
try testing.expectEqual(@as(T, 1), fifo.readItem().?);
try testing.expectEqual(@as(T, 0), fifo.readItem().?);
try testing.expectEqual(@as(T, 1), fifo.readItem().?);
try testing.expectEqual(@as(usize, 0), fifo.readableLength());
}
{
try fifo.writeItem(1);
try fifo.writeItem(1);
try fifo.writeItem(1);
try testing.expectEqual(@as(usize, 3), fifo.readableLength());
}
{
var readBuf: [3]T = undefined;
const n = fifo.read(&readBuf);
try testing.expectEqual(@as(usize, 3), n); // NOTE: It should be the number of items.
}
}
}

View File

@ -41,9 +41,9 @@ data_in_code_cmd: macho.linkedit_data_command = .{ .cmd = .DATA_IN_CODE },
uuid_cmd: macho.uuid_command = .{ .uuid = [_]u8{0} ** 16 }, uuid_cmd: macho.uuid_command = .{ .uuid = [_]u8{0} ** 16 },
codesig_cmd: macho.linkedit_data_command = .{ .cmd = .CODE_SIGNATURE }, codesig_cmd: macho.linkedit_data_command = .{ .cmd = .CODE_SIGNATURE },
pagezero_seg_index: ?u8 = null, pagezero_seg_index: ?u4 = null,
text_seg_index: ?u8 = null, text_seg_index: ?u4 = null,
linkedit_seg_index: ?u8 = null, linkedit_seg_index: ?u4 = null,
text_sect_index: ?u8 = null, text_sect_index: ?u8 = null,
data_sect_index: ?u8 = null, data_sect_index: ?u8 = null,
got_sect_index: ?u8 = null, got_sect_index: ?u8 = null,
@ -76,10 +76,10 @@ unwind_info: UnwindInfo = .{},
data_in_code: DataInCode = .{}, data_in_code: DataInCode = .{},
/// Tracked loadable segments during incremental linking. /// Tracked loadable segments during incremental linking.
zig_text_seg_index: ?u8 = null, zig_text_seg_index: ?u4 = null,
zig_const_seg_index: ?u8 = null, zig_const_seg_index: ?u4 = null,
zig_data_seg_index: ?u8 = null, zig_data_seg_index: ?u4 = null,
zig_bss_seg_index: ?u8 = null, zig_bss_seg_index: ?u4 = null,
/// Tracked section headers with incremental updates to Zig object. /// Tracked section headers with incremental updates to Zig object.
zig_text_sect_index: ?u8 = null, zig_text_sect_index: ?u8 = null,
@ -543,7 +543,7 @@ pub fn flush(
self.allocateSyntheticSymbols(); self.allocateSyntheticSymbols();
if (build_options.enable_logging) { if (build_options.enable_logging) {
state_log.debug("{}", .{self.dumpState()}); state_log.debug("{f}", .{self.dumpState()});
} }
// Beyond this point, everything has been allocated a virtual address and we can resolve // Beyond this point, everything has been allocated a virtual address and we can resolve
@ -591,6 +591,7 @@ pub fn flush(
error.NoSpaceLeft => unreachable, error.NoSpaceLeft => unreachable,
error.OutOfMemory => return error.OutOfMemory, error.OutOfMemory => return error.OutOfMemory,
error.LinkFailure => return error.LinkFailure, error.LinkFailure => return error.LinkFailure,
else => unreachable,
}; };
try self.writeHeader(ncmds, sizeofcmds); try self.writeHeader(ncmds, sizeofcmds);
self.writeUuid(uuid_cmd_offset, self.requiresCodeSig()) catch |err| switch (err) { self.writeUuid(uuid_cmd_offset, self.requiresCodeSig()) catch |err| switch (err) {
@ -677,12 +678,12 @@ fn dumpArgv(self: *MachO, comp: *Compilation) !void {
try argv.append("-platform_version"); try argv.append("-platform_version");
try argv.append(@tagName(self.platform.os_tag)); try argv.append(@tagName(self.platform.os_tag));
try argv.append(try std.fmt.allocPrint(arena, "{}", .{self.platform.version})); try argv.append(try std.fmt.allocPrint(arena, "{f}", .{self.platform.version}));
if (self.sdk_version) |ver| { if (self.sdk_version) |ver| {
try argv.append(try std.fmt.allocPrint(arena, "{d}.{d}", .{ ver.major, ver.minor })); try argv.append(try std.fmt.allocPrint(arena, "{d}.{d}", .{ ver.major, ver.minor }));
} else { } else {
try argv.append(try std.fmt.allocPrint(arena, "{}", .{self.platform.version})); try argv.append(try std.fmt.allocPrint(arena, "{f}", .{self.platform.version}));
} }
if (comp.sysroot) |syslibroot| { if (comp.sysroot) |syslibroot| {
@ -863,7 +864,7 @@ pub fn classifyInputFile(self: *MachO, input: link.Input) !void {
const path, const file = input.pathAndFile().?; const path, const file = input.pathAndFile().?;
// TODO don't classify now, it's too late. The input file has already been classified // TODO don't classify now, it's too late. The input file has already been classified
log.debug("classifying input file {}", .{path}); log.debug("classifying input file {f}", .{path});
const fh = try self.addFileHandle(file); const fh = try self.addFileHandle(file);
var buffer: [Archive.SARMAG]u8 = undefined; var buffer: [Archive.SARMAG]u8 = undefined;
@ -1074,7 +1075,7 @@ fn accessLibPath(
for (&[_][]const u8{ ".tbd", ".dylib", "" }) |ext| { for (&[_][]const u8{ ".tbd", ".dylib", "" }) |ext| {
test_path.clearRetainingCapacity(); test_path.clearRetainingCapacity();
try test_path.writer().print("{s}" ++ sep ++ "lib{s}{s}", .{ search_dir, name, ext }); try test_path.print("{s}" ++ sep ++ "lib{s}{s}", .{ search_dir, name, ext });
try checked_paths.append(try arena.dupe(u8, test_path.items)); try checked_paths.append(try arena.dupe(u8, test_path.items));
fs.cwd().access(test_path.items, .{}) catch |err| switch (err) { fs.cwd().access(test_path.items, .{}) catch |err| switch (err) {
error.FileNotFound => continue, error.FileNotFound => continue,
@ -1097,7 +1098,7 @@ fn accessFrameworkPath(
for (&[_][]const u8{ ".tbd", ".dylib", "" }) |ext| { for (&[_][]const u8{ ".tbd", ".dylib", "" }) |ext| {
test_path.clearRetainingCapacity(); test_path.clearRetainingCapacity();
try test_path.writer().print("{s}" ++ sep ++ "{s}.framework" ++ sep ++ "{s}{s}", .{ try test_path.print("{s}" ++ sep ++ "{s}.framework" ++ sep ++ "{s}{s}", .{
search_dir, search_dir,
name, name,
name, name,
@ -1178,9 +1179,9 @@ fn parseDependentDylibs(self: *MachO) !void {
for (&[_][]const u8{ ".tbd", ".dylib", "" }) |ext| { for (&[_][]const u8{ ".tbd", ".dylib", "" }) |ext| {
test_path.clearRetainingCapacity(); test_path.clearRetainingCapacity();
if (self.base.comp.sysroot) |root| { if (self.base.comp.sysroot) |root| {
try test_path.writer().print("{s}" ++ fs.path.sep_str ++ "{s}{s}", .{ root, path, ext }); try test_path.print("{s}" ++ fs.path.sep_str ++ "{s}{s}", .{ root, path, ext });
} else { } else {
try test_path.writer().print("{s}{s}", .{ path, ext }); try test_path.print("{s}{s}", .{ path, ext });
} }
try checked_paths.append(try arena.dupe(u8, test_path.items)); try checked_paths.append(try arena.dupe(u8, test_path.items));
fs.cwd().access(test_path.items, .{}) catch |err| switch (err) { fs.cwd().access(test_path.items, .{}) catch |err| switch (err) {
@ -1591,7 +1592,7 @@ fn reportUndefs(self: *MachO) !void {
const ref = refs.items[inote]; const ref = refs.items[inote];
const file = self.getFile(ref.file).?; const file = self.getFile(ref.file).?;
const atom = ref.getAtom(self).?; const atom = ref.getAtom(self).?;
err.addNote("referenced by {}:{s}", .{ file.fmtPath(), atom.getName(self) }); err.addNote("referenced by {f}:{s}", .{ file.fmtPath(), atom.getName(self) });
} }
if (refs.items.len > max_notes) { if (refs.items.len > max_notes) {
@ -2131,7 +2132,7 @@ fn initSegments(self: *MachO) !void {
mem.sort(Entry, entries.items, self, Entry.lessThan); mem.sort(Entry, entries.items, self, Entry.lessThan);
const backlinks = try gpa.alloc(u8, entries.items.len); const backlinks = try gpa.alloc(u4, entries.items.len);
defer gpa.free(backlinks); defer gpa.free(backlinks);
for (entries.items, 0..) |entry, i| { for (entries.items, 0..) |entry, i| {
backlinks[entry.index] = @intCast(i); backlinks[entry.index] = @intCast(i);
@ -2145,7 +2146,7 @@ fn initSegments(self: *MachO) !void {
self.segments.appendAssumeCapacity(segments[sorted.index]); self.segments.appendAssumeCapacity(segments[sorted.index]);
} }
for (&[_]*?u8{ for (&[_]*?u4{
&self.pagezero_seg_index, &self.pagezero_seg_index,
&self.text_seg_index, &self.text_seg_index,
&self.linkedit_seg_index, &self.linkedit_seg_index,
@ -2163,7 +2164,7 @@ fn initSegments(self: *MachO) !void {
for (slice.items(.header), slice.items(.segment_id)) |header, *seg_id| { for (slice.items(.header), slice.items(.segment_id)) |header, *seg_id| {
const segname = header.segName(); const segname = header.segName();
const segment_id = self.getSegmentByName(segname) orelse blk: { const segment_id = self.getSegmentByName(segname) orelse blk: {
const segment_id = @as(u8, @intCast(self.segments.items.len)); const segment_id: u4 = @intCast(self.segments.items.len);
const protection = getSegmentProt(segname); const protection = getSegmentProt(segname);
try self.segments.append(gpa, .{ try self.segments.append(gpa, .{
.cmdsize = @sizeOf(macho.segment_command_64), .cmdsize = @sizeOf(macho.segment_command_64),
@ -2526,10 +2527,8 @@ fn writeThunkWorker(self: *MachO, thunk: Thunk) void {
const doWork = struct { const doWork = struct {
fn doWork(th: Thunk, buffer: []u8, macho_file: *MachO) !void { fn doWork(th: Thunk, buffer: []u8, macho_file: *MachO) !void {
const off = try macho_file.cast(usize, th.value); var bw: Writer = .fixed(buffer[try macho_file.cast(usize, th.value)..][0..th.size()]);
const size = th.size(); try th.write(macho_file, &bw);
var stream = std.io.fixedBufferStream(buffer[off..][0..size]);
try th.write(macho_file, stream.writer());
} }
}.doWork; }.doWork;
const out = self.sections.items(.out)[thunk.out_n_sect].items; const out = self.sections.items(.out)[thunk.out_n_sect].items;
@ -2556,15 +2555,15 @@ fn writeSyntheticSectionWorker(self: *MachO, sect_id: u8, out: []u8) void {
const doWork = struct { const doWork = struct {
fn doWork(macho_file: *MachO, tag: Tag, buffer: []u8) !void { fn doWork(macho_file: *MachO, tag: Tag, buffer: []u8) !void {
var stream = std.io.fixedBufferStream(buffer); var bw: Writer = .fixed(buffer);
switch (tag) { switch (tag) {
.eh_frame => eh_frame.write(macho_file, buffer), .eh_frame => eh_frame.write(macho_file, buffer),
.unwind_info => try macho_file.unwind_info.write(macho_file, buffer), .unwind_info => try macho_file.unwind_info.write(macho_file, &bw),
.got => try macho_file.got.write(macho_file, stream.writer()), .got => try macho_file.got.write(macho_file, &bw),
.stubs => try macho_file.stubs.write(macho_file, stream.writer()), .stubs => try macho_file.stubs.write(macho_file, &bw),
.la_symbol_ptr => try macho_file.la_symbol_ptr.write(macho_file, stream.writer()), .la_symbol_ptr => try macho_file.la_symbol_ptr.write(macho_file, &bw),
.tlv_ptr => try macho_file.tlv_ptr.write(macho_file, stream.writer()), .tlv_ptr => try macho_file.tlv_ptr.write(macho_file, &bw),
.objc_stubs => try macho_file.objc_stubs.write(macho_file, stream.writer()), .objc_stubs => try macho_file.objc_stubs.write(macho_file, &bw),
} }
} }
}.doWork; }.doWork;
@ -2605,8 +2604,8 @@ fn updateLazyBindSizeWorker(self: *MachO) void {
try macho_file.lazy_bind_section.updateSize(macho_file); try macho_file.lazy_bind_section.updateSize(macho_file);
const sect_id = macho_file.stubs_helper_sect_index.?; const sect_id = macho_file.stubs_helper_sect_index.?;
const out = &macho_file.sections.items(.out)[sect_id]; const out = &macho_file.sections.items(.out)[sect_id];
var stream = std.io.fixedBufferStream(out.items); var bw: Writer = .fixed(out.items);
try macho_file.stubs_helper.write(macho_file, stream.writer()); try macho_file.stubs_helper.write(macho_file, &bw);
} }
}.doWork; }.doWork;
doWork(self) catch |err| doWork(self) catch |err|
@ -2665,46 +2664,49 @@ fn writeDyldInfo(self: *MachO) !void {
needed_size += cmd.lazy_bind_size; needed_size += cmd.lazy_bind_size;
needed_size += cmd.export_size; needed_size += cmd.export_size;
const buffer = try gpa.alloc(u8, needed_size); var bw: Writer = .fixed(try gpa.alloc(u8, needed_size));
defer gpa.free(buffer); defer gpa.free(bw.buffer);
@memset(buffer, 0); @memset(bw.buffer, 0);
var stream = std.io.fixedBufferStream(buffer); try self.rebase_section.write(&bw);
const writer = stream.writer(); bw.end = cmd.bind_off - base_off;
try self.bind_section.write(&bw);
try self.rebase_section.write(writer); bw.end = cmd.weak_bind_off - base_off;
try stream.seekTo(cmd.bind_off - base_off); try self.weak_bind_section.write(&bw);
try self.bind_section.write(writer); bw.end = cmd.lazy_bind_off - base_off;
try stream.seekTo(cmd.weak_bind_off - base_off); try self.lazy_bind_section.write(&bw);
try self.weak_bind_section.write(writer); bw.end = cmd.export_off - base_off;
try stream.seekTo(cmd.lazy_bind_off - base_off); try self.export_trie.write(&bw);
try self.lazy_bind_section.write(writer); try self.pwriteAll(bw.buffer, cmd.rebase_off);
try stream.seekTo(cmd.export_off - base_off);
try self.export_trie.write(writer);
try self.pwriteAll(buffer, cmd.rebase_off);
} }
pub fn writeDataInCode(self: *MachO) !void { pub fn writeDataInCode(self: *MachO) link.File.FlushError!void {
const tracy = trace(@src()); const tracy = trace(@src());
defer tracy.end(); defer tracy.end();
const gpa = self.base.comp.gpa; const gpa = self.base.comp.gpa;
const cmd = self.data_in_code_cmd; const cmd = self.data_in_code_cmd;
var buffer = try std.ArrayList(u8).initCapacity(gpa, self.data_in_code.size());
defer buffer.deinit(); var bw: Writer = .fixed(try gpa.alloc(u8, self.data_in_code.size()));
try self.data_in_code.write(self, buffer.writer()); defer gpa.free(bw.buffer);
try self.pwriteAll(buffer.items, cmd.dataoff);
try self.data_in_code.write(self, &bw);
assert(bw.end == bw.buffer.len);
try self.pwriteAll(bw.buffer, cmd.dataoff);
} }
fn writeIndsymtab(self: *MachO) !void { fn writeIndsymtab(self: *MachO) !void {
const tracy = trace(@src()); const tracy = trace(@src());
defer tracy.end(); defer tracy.end();
const gpa = self.base.comp.gpa; const gpa = self.base.comp.gpa;
const cmd = self.dysymtab_cmd; const cmd = self.dysymtab_cmd;
const needed_size = cmd.nindirectsyms * @sizeOf(u32);
var buffer = try std.ArrayList(u8).initCapacity(gpa, needed_size); var bw: Writer = .fixed(try gpa.alloc(u8, @sizeOf(u32) * cmd.nindirectsyms));
defer buffer.deinit(); defer gpa.free(bw.buffer);
try self.indsymtab.write(self, buffer.writer());
try self.pwriteAll(buffer.items, cmd.indirectsymoff); try self.indsymtab.write(self, &bw);
assert(bw.end == bw.buffer.len);
try self.pwriteAll(bw.buffer, cmd.indirectsymoff);
} }
pub fn writeSymtabToFile(self: *MachO) !void { pub fn writeSymtabToFile(self: *MachO) !void {
@ -2814,15 +2816,12 @@ fn calcSymtabSize(self: *MachO) !void {
} }
} }
fn writeLoadCommands(self: *MachO) !struct { usize, usize, u64 } { fn writeLoadCommands(self: *MachO) Writer.Error!struct { usize, usize, u64 } {
const comp = self.base.comp; const comp = self.base.comp;
const gpa = comp.gpa; const gpa = comp.gpa;
const needed_size = try load_commands.calcLoadCommandsSize(self, false);
const buffer = try gpa.alloc(u8, needed_size);
defer gpa.free(buffer);
var stream = std.io.fixedBufferStream(buffer); var bw: Writer = .fixed(try gpa.alloc(u8, try load_commands.calcLoadCommandsSize(self, false)));
const writer = stream.writer(); defer gpa.free(bw.buffer);
var ncmds: usize = 0; var ncmds: usize = 0;
@ -2831,26 +2830,26 @@ fn writeLoadCommands(self: *MachO) !struct { usize, usize, u64 } {
const slice = self.sections.slice(); const slice = self.sections.slice();
var sect_id: usize = 0; var sect_id: usize = 0;
for (self.segments.items) |seg| { for (self.segments.items) |seg| {
try writer.writeStruct(seg); try bw.writeStruct(seg);
for (slice.items(.header)[sect_id..][0..seg.nsects]) |header| { for (slice.items(.header)[sect_id..][0..seg.nsects]) |header| {
try writer.writeStruct(header); try bw.writeStruct(header);
} }
sect_id += seg.nsects; sect_id += seg.nsects;
} }
ncmds += self.segments.items.len; ncmds += self.segments.items.len;
} }
try writer.writeStruct(self.dyld_info_cmd); try bw.writeStruct(self.dyld_info_cmd);
ncmds += 1; ncmds += 1;
try writer.writeStruct(self.function_starts_cmd); try bw.writeStruct(self.function_starts_cmd);
ncmds += 1; ncmds += 1;
try writer.writeStruct(self.data_in_code_cmd); try bw.writeStruct(self.data_in_code_cmd);
ncmds += 1; ncmds += 1;
try writer.writeStruct(self.symtab_cmd); try bw.writeStruct(self.symtab_cmd);
ncmds += 1; ncmds += 1;
try writer.writeStruct(self.dysymtab_cmd); try bw.writeStruct(self.dysymtab_cmd);
ncmds += 1; ncmds += 1;
try load_commands.writeDylinkerLC(writer); try load_commands.writeDylinkerLC(&bw);
ncmds += 1; ncmds += 1;
if (self.getInternalObject()) |obj| { if (self.getInternalObject()) |obj| {
@ -2861,7 +2860,7 @@ fn writeLoadCommands(self: *MachO) !struct { usize, usize, u64 } {
0 0
else else
@as(u32, @intCast(sym.getAddress(.{ .stubs = true }, self) - seg.vmaddr)); @as(u32, @intCast(sym.getAddress(.{ .stubs = true }, self) - seg.vmaddr));
try writer.writeStruct(macho.entry_point_command{ try bw.writeStruct(macho.entry_point_command{
.entryoff = entryoff, .entryoff = entryoff,
.stacksize = self.base.stack_size, .stacksize = self.base.stack_size,
}); });
@ -2870,35 +2869,35 @@ fn writeLoadCommands(self: *MachO) !struct { usize, usize, u64 } {
} }
if (self.base.isDynLib()) { if (self.base.isDynLib()) {
try load_commands.writeDylibIdLC(self, writer); try load_commands.writeDylibIdLC(self, &bw);
ncmds += 1; ncmds += 1;
} }
for (self.rpath_list) |rpath| { for (self.rpath_list) |rpath| {
try load_commands.writeRpathLC(rpath, writer); try load_commands.writeRpathLC(&bw, rpath);
ncmds += 1; ncmds += 1;
} }
if (comp.config.any_sanitize_thread) { if (comp.config.any_sanitize_thread) {
const path = try comp.tsan_lib.?.full_object_path.toString(gpa); const path = try comp.tsan_lib.?.full_object_path.toString(gpa);
defer gpa.free(path); defer gpa.free(path);
const rpath = std.fs.path.dirname(path) orelse "."; const rpath = std.fs.path.dirname(path) orelse ".";
try load_commands.writeRpathLC(rpath, writer); try load_commands.writeRpathLC(&bw, rpath);
ncmds += 1; ncmds += 1;
} }
try writer.writeStruct(macho.source_version_command{ .version = 0 }); try bw.writeStruct(macho.source_version_command{ .version = 0 });
ncmds += 1; ncmds += 1;
if (self.platform.isBuildVersionCompatible()) { if (self.platform.isBuildVersionCompatible()) {
try load_commands.writeBuildVersionLC(self.platform, self.sdk_version, writer); try load_commands.writeBuildVersionLC(&bw, self.platform, self.sdk_version);
ncmds += 1; ncmds += 1;
} else { } else {
try load_commands.writeVersionMinLC(self.platform, self.sdk_version, writer); try load_commands.writeVersionMinLC(&bw, self.platform, self.sdk_version);
ncmds += 1; ncmds += 1;
} }
const uuid_cmd_offset = @sizeOf(macho.mach_header_64) + stream.pos; const uuid_cmd_offset = @sizeOf(macho.mach_header_64) + bw.count;
try writer.writeStruct(self.uuid_cmd); try bw.writeStruct(self.uuid_cmd);
ncmds += 1; ncmds += 1;
for (self.dylibs.items) |index| { for (self.dylibs.items) |index| {
@ -2916,20 +2915,19 @@ fn writeLoadCommands(self: *MachO) !struct { usize, usize, u64 } {
.timestamp = dylib_id.timestamp, .timestamp = dylib_id.timestamp,
.current_version = dylib_id.current_version, .current_version = dylib_id.current_version,
.compatibility_version = dylib_id.compatibility_version, .compatibility_version = dylib_id.compatibility_version,
}, writer); }, &bw);
ncmds += 1; ncmds += 1;
} }
if (self.requiresCodeSig()) { if (self.requiresCodeSig()) {
try writer.writeStruct(self.codesig_cmd); try bw.writeStruct(self.codesig_cmd);
ncmds += 1; ncmds += 1;
} }
assert(stream.pos == needed_size); assert(bw.end == bw.buffer.len);
try self.pwriteAll(bw.buffer, @sizeOf(macho.mach_header_64));
try self.pwriteAll(buffer, @sizeOf(macho.mach_header_64)); return .{ ncmds, bw.end, uuid_cmd_offset };
return .{ ncmds, buffer.len, uuid_cmd_offset };
} }
fn writeHeader(self: *MachO, ncmds: usize, sizeofcmds: usize) !void { fn writeHeader(self: *MachO, ncmds: usize, sizeofcmds: usize) !void {
@ -3012,27 +3010,27 @@ pub fn writeCodeSignaturePadding(self: *MachO, code_sig: *CodeSignature) !void {
} }
pub fn writeCodeSignature(self: *MachO, code_sig: *CodeSignature) !void { pub fn writeCodeSignature(self: *MachO, code_sig: *CodeSignature) !void {
const gpa = self.base.comp.gpa;
const seg = self.getTextSegment(); const seg = self.getTextSegment();
const offset = self.codesig_cmd.dataoff; const offset = self.codesig_cmd.dataoff;
var buffer = std.ArrayList(u8).init(self.base.comp.gpa); var bw: Writer = .fixed(try gpa.alloc(u8, code_sig.size()));
defer buffer.deinit(); defer gpa.free(bw.buffer);
try buffer.ensureTotalCapacityPrecise(code_sig.size());
try code_sig.writeAdhocSignature(self, .{ try code_sig.writeAdhocSignature(self, .{
.file = self.base.file.?, .file = self.base.file.?,
.exec_seg_base = seg.fileoff, .exec_seg_base = seg.fileoff,
.exec_seg_limit = seg.filesize, .exec_seg_limit = seg.filesize,
.file_size = offset, .file_size = offset,
.dylib = self.base.isDynLib(), .dylib = self.base.isDynLib(),
}, buffer.writer()); }, &bw);
assert(buffer.items.len == code_sig.size());
log.debug("writing code signature from 0x{x} to 0x{x}", .{ log.debug("writing code signature from 0x{x} to 0x{x}", .{
offset, offset,
offset + buffer.items.len, offset + bw.end,
}); });
try self.pwriteAll(buffer.items, offset); assert(bw.end == bw.buffer.len);
try self.pwriteAll(bw.buffer, offset);
} }
pub fn updateFunc( pub fn updateFunc(
@ -3341,7 +3339,7 @@ fn initMetadata(self: *MachO, options: InitMetadataOptions) !void {
} }
const appendSect = struct { const appendSect = struct {
fn appendSect(macho_file: *MachO, sect_id: u8, seg_id: u8) void { fn appendSect(macho_file: *MachO, sect_id: u8, seg_id: u4) void {
const sect = &macho_file.sections.items(.header)[sect_id]; const sect = &macho_file.sections.items(.header)[sect_id];
const seg = macho_file.segments.items[seg_id]; const seg = macho_file.segments.items[seg_id];
sect.addr = seg.vmaddr; sect.addr = seg.vmaddr;
@ -3600,7 +3598,7 @@ inline fn requiresThunks(self: MachO) bool {
} }
pub fn isZigSegment(self: MachO, seg_id: u8) bool { pub fn isZigSegment(self: MachO, seg_id: u8) bool {
inline for (&[_]?u8{ inline for (&[_]?u4{
self.zig_text_seg_index, self.zig_text_seg_index,
self.zig_const_seg_index, self.zig_const_seg_index,
self.zig_data_seg_index, self.zig_data_seg_index,
@ -3648,9 +3646,9 @@ pub fn addSegment(self: *MachO, name: []const u8, opts: struct {
fileoff: u64 = 0, fileoff: u64 = 0,
filesize: u64 = 0, filesize: u64 = 0,
prot: macho.vm_prot_t = macho.PROT.NONE, prot: macho.vm_prot_t = macho.PROT.NONE,
}) error{OutOfMemory}!u8 { }) error{OutOfMemory}!u4 {
const gpa = self.base.comp.gpa; const gpa = self.base.comp.gpa;
const index = @as(u8, @intCast(self.segments.items.len)); const index: u4 = @intCast(self.segments.items.len);
try self.segments.append(gpa, .{ try self.segments.append(gpa, .{
.segname = makeStaticString(name), .segname = makeStaticString(name),
.vmaddr = opts.vmaddr, .vmaddr = opts.vmaddr,
@ -3700,9 +3698,9 @@ pub fn makeStaticString(bytes: []const u8) [16]u8 {
return buf; return buf;
} }
pub fn getSegmentByName(self: MachO, segname: []const u8) ?u8 { pub fn getSegmentByName(self: MachO, segname: []const u8) ?u4 {
for (self.segments.items, 0..) |seg, i| { for (self.segments.items, 0..) |seg, i| {
if (mem.eql(u8, segname, seg.segName())) return @as(u8, @intCast(i)); if (mem.eql(u8, segname, seg.segName())) return @intCast(i);
} else return null; } else return null;
} }
@ -3791,7 +3789,7 @@ pub fn reportParseError2(
const diags = &self.base.comp.link_diags; const diags = &self.base.comp.link_diags;
var err = try diags.addErrorWithNotes(1); var err = try diags.addErrorWithNotes(1);
try err.addMsg(format, args); try err.addMsg(format, args);
err.addNote("while parsing {}", .{self.getFile(file_index).?.fmtPath()}); err.addNote("while parsing {f}", .{self.getFile(file_index).?.fmtPath()});
} }
fn reportMissingDependencyError( fn reportMissingDependencyError(
@ -3806,7 +3804,7 @@ fn reportMissingDependencyError(
var err = try diags.addErrorWithNotes(2 + checked_paths.len); var err = try diags.addErrorWithNotes(2 + checked_paths.len);
try err.addMsg(format, args); try err.addMsg(format, args);
err.addNote("while resolving {s}", .{path}); err.addNote("while resolving {s}", .{path});
err.addNote("a dependency of {}", .{self.getFile(parent).?.fmtPath()}); err.addNote("a dependency of {f}", .{self.getFile(parent).?.fmtPath()});
for (checked_paths) |p| { for (checked_paths) |p| {
err.addNote("tried {s}", .{p}); err.addNote("tried {s}", .{p});
} }
@ -3823,7 +3821,7 @@ fn reportDependencyError(
var err = try diags.addErrorWithNotes(2); var err = try diags.addErrorWithNotes(2);
try err.addMsg(format, args); try err.addMsg(format, args);
err.addNote("while parsing {s}", .{path}); err.addNote("while parsing {s}", .{path});
err.addNote("a dependency of {}", .{self.getFile(parent).?.fmtPath()}); err.addNote("a dependency of {f}", .{self.getFile(parent).?.fmtPath()});
} }
fn reportDuplicates(self: *MachO) error{ HasDuplicates, OutOfMemory }!void { fn reportDuplicates(self: *MachO) error{ HasDuplicates, OutOfMemory }!void {
@ -3853,12 +3851,12 @@ fn reportDuplicates(self: *MachO) error{ HasDuplicates, OutOfMemory }!void {
var err = try diags.addErrorWithNotes(nnotes + 1); var err = try diags.addErrorWithNotes(nnotes + 1);
try err.addMsg("duplicate symbol definition: {s}", .{sym.getName(self)}); try err.addMsg("duplicate symbol definition: {s}", .{sym.getName(self)});
err.addNote("defined by {}", .{sym.getFile(self).?.fmtPath()}); err.addNote("defined by {f}", .{sym.getFile(self).?.fmtPath()});
var inote: usize = 0; var inote: usize = 0;
while (inote < @min(notes.items.len, max_notes)) : (inote += 1) { while (inote < @min(notes.items.len, max_notes)) : (inote += 1) {
const file = self.getFile(notes.items[inote]).?; const file = self.getFile(notes.items[inote]).?;
err.addNote("defined by {}", .{file.fmtPath()}); err.addNote("defined by {f}", .{file.fmtPath()});
} }
if (notes.items.len > max_notes) { if (notes.items.len > max_notes) {
@ -3900,35 +3898,28 @@ pub fn ptraceDetach(self: *MachO, pid: std.posix.pid_t) !void {
self.hot_state.mach_task = null; self.hot_state.mach_task = null;
} }
pub fn dumpState(self: *MachO) std.fmt.Formatter(fmtDumpState) { pub fn dumpState(self: *MachO) std.fmt.Formatter(*MachO, fmtDumpState) {
return .{ .data = self }; return .{ .data = self };
} }
fn fmtDumpState( fn fmtDumpState(self: *MachO, w: *Writer) Writer.Error!void {
self: *MachO,
comptime unused_fmt_string: []const u8,
options: std.fmt.FormatOptions,
writer: anytype,
) !void {
_ = options;
_ = unused_fmt_string;
if (self.getZigObject()) |zo| { if (self.getZigObject()) |zo| {
try writer.print("zig_object({d}) : {s}\n", .{ zo.index, zo.basename }); try w.print("zig_object({d}) : {s}\n", .{ zo.index, zo.basename });
try writer.print("{}{}\n", .{ try w.print("{f}{f}\n", .{
zo.fmtAtoms(self), zo.fmtAtoms(self),
zo.fmtSymtab(self), zo.fmtSymtab(self),
}); });
} }
for (self.objects.items) |index| { for (self.objects.items) |index| {
const object = self.getFile(index).?.object; const object = self.getFile(index).?.object;
try writer.print("object({d}) : {} : has_debug({})", .{ try w.print("object({d}) : {f} : has_debug({})", .{
index, index,
object.fmtPath(), object.fmtPath(),
object.hasDebugInfo(), object.hasDebugInfo(),
}); });
if (!object.alive) try writer.writeAll(" : ([*])"); if (!object.alive) try w.writeAll(" : ([*])");
try writer.writeByte('\n'); try w.writeByte('\n');
try writer.print("{}{}{}{}{}\n", .{ try w.print("{f}{f}{f}{f}{f}\n", .{
object.fmtAtoms(self), object.fmtAtoms(self),
object.fmtCies(self), object.fmtCies(self),
object.fmtFdes(self), object.fmtFdes(self),
@ -3938,48 +3929,41 @@ fn fmtDumpState(
} }
for (self.dylibs.items) |index| { for (self.dylibs.items) |index| {
const dylib = self.getFile(index).?.dylib; const dylib = self.getFile(index).?.dylib;
try writer.print("dylib({d}) : {} : needed({}) : weak({})", .{ try w.print("dylib({d}) : {f} : needed({}) : weak({})", .{
index, index,
@as(Path, dylib.path), @as(Path, dylib.path),
dylib.needed, dylib.needed,
dylib.weak, dylib.weak,
}); });
if (!dylib.isAlive(self)) try writer.writeAll(" : ([*])"); if (!dylib.isAlive(self)) try w.writeAll(" : ([*])");
try writer.writeByte('\n'); try w.writeByte('\n');
try writer.print("{}\n", .{dylib.fmtSymtab(self)}); try w.print("{f}\n", .{dylib.fmtSymtab(self)});
} }
if (self.getInternalObject()) |internal| { if (self.getInternalObject()) |internal| {
try writer.print("internal({d}) : internal\n", .{internal.index}); try w.print("internal({d}) : internal\n", .{internal.index});
try writer.print("{}{}\n", .{ internal.fmtAtoms(self), internal.fmtSymtab(self) }); try w.print("{f}{f}\n", .{ internal.fmtAtoms(self), internal.fmtSymtab(self) });
} }
try writer.writeAll("thunks\n"); try w.writeAll("thunks\n");
for (self.thunks.items, 0..) |thunk, index| { for (self.thunks.items, 0..) |thunk, index| {
try writer.print("thunk({d}) : {}\n", .{ index, thunk.fmt(self) }); try w.print("thunk({d}) : {f}\n", .{ index, thunk.fmt(self) });
} }
try writer.print("stubs\n{}\n", .{self.stubs.fmt(self)}); try w.print("stubs\n{f}\n", .{self.stubs.fmt(self)});
try writer.print("objc_stubs\n{}\n", .{self.objc_stubs.fmt(self)}); try w.print("objc_stubs\n{f}\n", .{self.objc_stubs.fmt(self)});
try writer.print("got\n{}\n", .{self.got.fmt(self)}); try w.print("got\n{f}\n", .{self.got.fmt(self)});
try writer.print("tlv_ptr\n{}\n", .{self.tlv_ptr.fmt(self)}); try w.print("tlv_ptr\n{f}\n", .{self.tlv_ptr.fmt(self)});
try writer.writeByte('\n'); try w.writeByte('\n');
try writer.print("sections\n{}\n", .{self.fmtSections()}); try w.print("sections\n{f}\n", .{self.fmtSections()});
try writer.print("segments\n{}\n", .{self.fmtSegments()}); try w.print("segments\n{f}\n", .{self.fmtSegments()});
} }
fn fmtSections(self: *MachO) std.fmt.Formatter(formatSections) { fn fmtSections(self: *MachO) std.fmt.Formatter(*MachO, formatSections) {
return .{ .data = self }; return .{ .data = self };
} }
fn formatSections( fn formatSections(self: *MachO, w: *Writer) Writer.Error!void {
self: *MachO,
comptime unused_fmt_string: []const u8,
options: std.fmt.FormatOptions,
writer: anytype,
) !void {
_ = options;
_ = unused_fmt_string;
const slice = self.sections.slice(); const slice = self.sections.slice();
for (slice.items(.header), slice.items(.segment_id), 0..) |header, seg_id, i| { for (slice.items(.header), slice.items(.segment_id), 0..) |header, seg_id, i| {
try writer.print( try w.print(
"sect({d}) : seg({d}) : {s},{s} : @{x} ({x}) : align({x}) : size({x}) : relocs({x};{d})\n", "sect({d}) : seg({d}) : {s},{s} : @{x} ({x}) : align({x}) : size({x}) : relocs({x};{d})\n",
.{ .{
i, seg_id, header.segName(), header.sectName(), header.addr, header.offset, i, seg_id, header.segName(), header.sectName(), header.addr, header.offset,
@ -3989,38 +3973,24 @@ fn formatSections(
} }
} }
fn fmtSegments(self: *MachO) std.fmt.Formatter(formatSegments) { fn fmtSegments(self: *MachO) std.fmt.Formatter(*MachO, formatSegments) {
return .{ .data = self }; return .{ .data = self };
} }
fn formatSegments( fn formatSegments(self: *MachO, w: *Writer) Writer.Error!void {
self: *MachO,
comptime unused_fmt_string: []const u8,
options: std.fmt.FormatOptions,
writer: anytype,
) !void {
_ = options;
_ = unused_fmt_string;
for (self.segments.items, 0..) |seg, i| { for (self.segments.items, 0..) |seg, i| {
try writer.print("seg({d}) : {s} : @{x}-{x} ({x}-{x})\n", .{ try w.print("seg({d}) : {s} : @{x}-{x} ({x}-{x})\n", .{
i, seg.segName(), seg.vmaddr, seg.vmaddr + seg.vmsize, i, seg.segName(), seg.vmaddr, seg.vmaddr + seg.vmsize,
seg.fileoff, seg.fileoff + seg.filesize, seg.fileoff, seg.fileoff + seg.filesize,
}); });
} }
} }
pub fn fmtSectType(tt: u8) std.fmt.Formatter(formatSectType) { pub fn fmtSectType(tt: u8) std.fmt.Formatter(u8, formatSectType) {
return .{ .data = tt }; return .{ .data = tt };
} }
fn formatSectType( fn formatSectType(tt: u8, w: *Writer) Writer.Error!void {
tt: u8,
comptime unused_fmt_string: []const u8,
options: std.fmt.FormatOptions,
writer: anytype,
) !void {
_ = options;
_ = unused_fmt_string;
const name = switch (tt) { const name = switch (tt) {
macho.S_REGULAR => "REGULAR", macho.S_REGULAR => "REGULAR",
macho.S_ZEROFILL => "ZEROFILL", macho.S_ZEROFILL => "ZEROFILL",
@ -4044,9 +4014,9 @@ fn formatSectType(
macho.S_THREAD_LOCAL_VARIABLE_POINTERS => "THREAD_LOCAL_VARIABLE_POINTERS", macho.S_THREAD_LOCAL_VARIABLE_POINTERS => "THREAD_LOCAL_VARIABLE_POINTERS",
macho.S_THREAD_LOCAL_INIT_FUNCTION_POINTERS => "THREAD_LOCAL_INIT_FUNCTION_POINTERS", macho.S_THREAD_LOCAL_INIT_FUNCTION_POINTERS => "THREAD_LOCAL_INIT_FUNCTION_POINTERS",
macho.S_INIT_FUNC_OFFSETS => "INIT_FUNC_OFFSETS", macho.S_INIT_FUNC_OFFSETS => "INIT_FUNC_OFFSETS",
else => |x| return writer.print("UNKNOWN({x})", .{x}), else => |x| return w.print("UNKNOWN({x})", .{x}),
}; };
try writer.print("{s}", .{name}); try w.print("{s}", .{name});
} }
const is_hot_update_compatible = switch (builtin.target.os.tag) { const is_hot_update_compatible = switch (builtin.target.os.tag) {
@ -4058,7 +4028,7 @@ const default_entry_symbol_name = "_main";
const Section = struct { const Section = struct {
header: macho.section_64, header: macho.section_64,
segment_id: u8, segment_id: u4,
atoms: std.ArrayListUnmanaged(Ref) = .empty, atoms: std.ArrayListUnmanaged(Ref) = .empty,
free_list: std.ArrayListUnmanaged(Atom.Index) = .empty, free_list: std.ArrayListUnmanaged(Atom.Index) = .empty,
last_atom_index: Atom.Index = 0, last_atom_index: Atom.Index = 0,
@ -4279,28 +4249,21 @@ pub const Platform = struct {
return false; return false;
} }
pub fn fmtTarget(plat: Platform, cpu_arch: std.Target.Cpu.Arch) std.fmt.Formatter(formatTarget) { pub fn fmtTarget(plat: Platform, cpu_arch: std.Target.Cpu.Arch) std.fmt.Formatter(Format, Format.target) {
return .{ .data = .{ .platform = plat, .cpu_arch = cpu_arch } }; return .{ .data = .{ .platform = plat, .cpu_arch = cpu_arch } };
} }
const FmtCtx = struct { const Format = struct {
platform: Platform, platform: Platform,
cpu_arch: std.Target.Cpu.Arch, cpu_arch: std.Target.Cpu.Arch,
};
pub fn formatTarget( pub fn target(f: Format, w: *Writer) Writer.Error!void {
ctx: FmtCtx, try w.print("{s}-{s}", .{ @tagName(f.cpu_arch), @tagName(f.platform.os_tag) });
comptime unused_fmt_string: []const u8, if (f.platform.abi != .none) {
options: std.fmt.FormatOptions, try w.print("-{s}", .{@tagName(f.platform.abi)});
writer: anytype,
) !void {
_ = unused_fmt_string;
_ = options;
try writer.print("{s}-{s}", .{ @tagName(ctx.cpu_arch), @tagName(ctx.platform.os_tag) });
if (ctx.platform.abi != .none) {
try writer.print("-{s}", .{@tagName(ctx.platform.abi)});
} }
} }
};
/// Caller owns the memory. /// Caller owns the memory.
pub fn allocPrintTarget(plat: Platform, gpa: Allocator, cpu_arch: std.Target.Cpu.Arch) error{OutOfMemory}![]u8 { pub fn allocPrintTarget(plat: Platform, gpa: Allocator, cpu_arch: std.Target.Cpu.Arch) error{OutOfMemory}![]u8 {
@ -4390,7 +4353,7 @@ fn inferSdkVersion(comp: *Compilation, sdk_layout: SdkLayout) ?std.SemanticVersi
// The file/property is also available with vendored libc. // The file/property is also available with vendored libc.
fn readSdkVersionFromSettings(arena: Allocator, dir: []const u8) ![]const u8 { fn readSdkVersionFromSettings(arena: Allocator, dir: []const u8) ![]const u8 {
const sdk_path = try fs.path.join(arena, &.{ dir, "SDKSettings.json" }); const sdk_path = try fs.path.join(arena, &.{ dir, "SDKSettings.json" });
const contents = try fs.cwd().readFileAlloc(arena, sdk_path, std.math.maxInt(u16)); const contents = try fs.cwd().readFileAlloc(sdk_path, arena, .limited(std.math.maxInt(u16)));
const parsed = try std.json.parseFromSlice(std.json.Value, arena, contents, .{}); const parsed = try std.json.parseFromSlice(std.json.Value, arena, contents, .{});
if (parsed.value.object.get("MinimalDisplayName")) |ver| return ver.string; if (parsed.value.object.get("MinimalDisplayName")) |ver| return ver.string;
return error.SdkVersionFailure; return error.SdkVersionFailure;
@ -4406,7 +4369,7 @@ fn parseSdkVersion(raw: []const u8) ?std.SemanticVersion {
}; };
const parseNext = struct { const parseNext = struct {
fn parseNext(it: anytype) ?u16 { fn parseNext(it: *std.mem.SplitIterator(u8, .any)) ?u16 {
const nn = it.next() orelse return null; const nn = it.next() orelse return null;
return std.fmt.parseInt(u16, nn, 10) catch null; return std.fmt.parseInt(u16, nn, 10) catch null;
} }
@ -4507,15 +4470,9 @@ pub const Ref = struct {
}; };
} }
pub fn format( pub fn format(ref: Ref, bw: *Writer, comptime unused_fmt_string: []const u8) Writer.Error!void {
ref: Ref, comptime assert(unused_fmt_string.len == 0);
comptime unused_fmt_string: []const u8, try bw.print("%{d} in file({d})", .{ ref.index, ref.file });
options: std.fmt.FormatOptions,
writer: anytype,
) !void {
_ = unused_fmt_string;
_ = options;
try writer.print("%{d} in file({d})", .{ ref.index, ref.file });
} }
}; };
@ -5315,7 +5272,7 @@ fn createThunks(macho_file: *MachO, sect_id: u8) !void {
try scanThunkRelocs(thunk_index, gpa, atoms[start..i], macho_file); try scanThunkRelocs(thunk_index, gpa, atoms[start..i], macho_file);
thunk.value = advanceSection(header, thunk.size(), .@"4"); thunk.value = advanceSection(header, thunk.size(), .@"4");
log.debug("thunk({d}) : {}", .{ thunk_index, thunk.fmt(macho_file) }); log.debug("thunk({d}) : {f}", .{ thunk_index, thunk.fmt(macho_file) });
} }
} }
@ -5360,8 +5317,11 @@ fn isReachable(atom: *const Atom, rel: Relocation, macho_file: *MachO) bool {
pub fn pwriteAll(macho_file: *MachO, bytes: []const u8, offset: u64) error{LinkFailure}!void { pub fn pwriteAll(macho_file: *MachO, bytes: []const u8, offset: u64) error{LinkFailure}!void {
const comp = macho_file.base.comp; const comp = macho_file.base.comp;
const diags = &comp.link_diags; const diags = &comp.link_diags;
macho_file.base.file.?.pwriteAll(bytes, offset) catch |err| { var fw = macho_file.base.file.?.writer();
return diags.fail("failed to write: {s}", .{@errorName(err)}); fw.pos = offset;
var bw = fw.interface().unbuffered();
bw.writeAll(bytes) catch |err| switch (err) {
error.WriteFailed => return diags.fail("failed to write: {s}", .{@errorName(fw.err.?)}),
}; };
} }
@ -5414,6 +5374,7 @@ const macho = std.macho;
const math = std.math; const math = std.math;
const mem = std.mem; const mem = std.mem;
const meta = std.meta; const meta = std.meta;
const Writer = std.io.Writer;
const aarch64 = @import("../arch/aarch64/bits.zig"); const aarch64 = @import("../arch/aarch64/bits.zig");
const bind = @import("MachO/dyld_info/bind.zig"); const bind = @import("MachO/dyld_info/bind.zig");

View File

@ -71,53 +71,29 @@ pub fn unpack(self: *Archive, macho_file: *MachO, path: Path, handle_index: File
.mtime = hdr.date() catch 0, .mtime = hdr.date() catch 0,
}; };
log.debug("extracting object '{}' from archive '{}'", .{ object.path, path }); log.debug("extracting object '{f}' from archive '{f}'", .{ object.path, path });
try self.objects.append(gpa, object); try self.objects.append(gpa, object);
} }
} }
pub fn writeHeader( pub fn writeHeader(
bw: *Writer,
object_name: []const u8, object_name: []const u8,
object_size: usize, object_size: usize,
format: Format, format: Format,
writer: anytype, ) Writer.Error!void {
) !void { var hdr: ar_hdr = undefined;
var hdr: ar_hdr = .{ @memset(mem.asBytes(&hdr), ' ');
.ar_name = undefined, inline for (@typeInfo(ar_hdr).@"struct".fields) |field| @field(hdr, field.name)[0] = '0';
.ar_date = undefined,
.ar_uid = undefined,
.ar_gid = undefined,
.ar_mode = undefined,
.ar_size = undefined,
.ar_fmag = undefined,
};
@memset(mem.asBytes(&hdr), 0x20);
inline for (@typeInfo(ar_hdr).@"struct".fields) |field| {
var stream = std.io.fixedBufferStream(&@field(hdr, field.name));
stream.writer().print("0", .{}) catch unreachable;
}
@memcpy(&hdr.ar_fmag, ARFMAG); @memcpy(&hdr.ar_fmag, ARFMAG);
const object_name_len = mem.alignForward(usize, object_name.len + 1, ptrWidth(format)); const object_name_len = mem.alignForward(usize, object_name.len + 1, ptrWidth(format));
_ = std.fmt.bufPrint(&hdr.ar_name, "#1/{d}", .{object_name_len}) catch unreachable;
const total_object_size = object_size + object_name_len; const total_object_size = object_size + object_name_len;
_ = std.fmt.bufPrint(&hdr.ar_size, "{d}", .{total_object_size}) catch unreachable;
{ try bw.writeStruct(hdr);
var stream = std.io.fixedBufferStream(&hdr.ar_name); try bw.writeAll(object_name);
stream.writer().print("#1/{d}", .{object_name_len}) catch unreachable; try bw.splatByteAll(0, object_name_len - object_name.len);
}
{
var stream = std.io.fixedBufferStream(&hdr.ar_size);
stream.writer().print("{d}", .{total_object_size}) catch unreachable;
}
try writer.writeAll(mem.asBytes(&hdr));
try writer.print("{s}\x00", .{object_name});
const padding = object_name_len - object_name.len - 1;
if (padding > 0) {
try writer.writeByteNTimes(0, padding);
}
} }
// Archive files start with the ARMAG identifying string. Then follows a // Archive files start with the ARMAG identifying string. Then follows a
@ -201,12 +177,12 @@ pub const ArSymtab = struct {
return ptr_width + ar.entries.items.len * 2 * ptr_width + ptr_width + mem.alignForward(usize, ar.strtab.buffer.items.len, ptr_width); return ptr_width + ar.entries.items.len * 2 * ptr_width + ptr_width + mem.alignForward(usize, ar.strtab.buffer.items.len, ptr_width);
} }
pub fn write(ar: ArSymtab, format: Format, macho_file: *MachO, writer: anytype) !void { pub fn write(ar: ArSymtab, bw: *Writer, format: Format, macho_file: *MachO) Writer.Error!void {
const ptr_width = ptrWidth(format); const ptr_width = ptrWidth(format);
// Header // Header
try writeHeader(SYMDEF, ar.size(format), format, writer); try writeHeader(bw, SYMDEF, ar.size(format), format);
// Symtab size // Symtab size
try writeInt(format, ar.entries.items.len * 2 * ptr_width, writer); try writeInt(bw, format, ar.entries.items.len * 2 * ptr_width);
// Symtab entries // Symtab entries
for (ar.entries.items) |entry| { for (ar.entries.items) |entry| {
const file_off = switch (macho_file.getFile(entry.file).?) { const file_off = switch (macho_file.getFile(entry.file).?) {
@ -215,46 +191,36 @@ pub const ArSymtab = struct {
else => unreachable, else => unreachable,
}; };
// Name offset // Name offset
try writeInt(format, entry.off, writer); try writeInt(bw, format, entry.off);
// File offset // File offset
try writeInt(format, file_off, writer); try writeInt(bw, format, file_off);
} }
// Strtab size // Strtab size
const strtab_size = mem.alignForward(usize, ar.strtab.buffer.items.len, ptr_width); const strtab_size = mem.alignForward(usize, ar.strtab.buffer.items.len, ptr_width);
const padding = strtab_size - ar.strtab.buffer.items.len; try writeInt(bw, format, strtab_size);
try writeInt(format, strtab_size, writer);
// Strtab // Strtab
try writer.writeAll(ar.strtab.buffer.items); try bw.writeAll(ar.strtab.buffer.items);
if (padding > 0) { try bw.splatByteAll(0, strtab_size - ar.strtab.buffer.items.len);
try writer.writeByteNTimes(0, padding);
}
} }
const FormatContext = struct { const PrintFormat = struct {
ar: ArSymtab, ar: ArSymtab,
macho_file: *MachO, macho_file: *MachO,
};
pub fn fmt(ar: ArSymtab, macho_file: *MachO) std.fmt.Formatter(format2) { fn default(f: PrintFormat, bw: *Writer) Writer.Error!void {
return .{ .data = .{ .ar = ar, .macho_file = macho_file } }; const ar = f.ar;
} const macho_file = f.macho_file;
fn format2(
ctx: FormatContext,
comptime unused_fmt_string: []const u8,
options: std.fmt.FormatOptions,
writer: anytype,
) !void {
_ = unused_fmt_string;
_ = options;
const ar = ctx.ar;
const macho_file = ctx.macho_file;
for (ar.entries.items, 0..) |entry, i| { for (ar.entries.items, 0..) |entry, i| {
const name = ar.strtab.getAssumeExists(entry.off); const name = ar.strtab.getAssumeExists(entry.off);
const file = macho_file.getFile(entry.file).?; const file = macho_file.getFile(entry.file).?;
try writer.print(" {d}: {s} in file({d})({})\n", .{ i, name, entry.file, file.fmtPath() }); try bw.print(" {d}: {s} in file({d})({f})\n", .{ i, name, entry.file, file.fmtPath() });
} }
} }
};
pub fn fmt(ar: ArSymtab, macho_file: *MachO) std.fmt.Formatter(PrintFormat, PrintFormat.default) {
return .{ .data = .{ .ar = ar, .macho_file = macho_file } };
}
const Entry = struct { const Entry = struct {
/// Symbol name offset /// Symbol name offset
@ -282,10 +248,10 @@ pub fn ptrWidth(format: Format) usize {
}; };
} }
pub fn writeInt(format: Format, value: u64, writer: anytype) !void { pub fn writeInt(bw: *Writer, format: Format, value: u64) Writer.Error!void {
switch (format) { switch (format) {
.p32 => try writer.writeInt(u32, std.math.cast(u32, value) orelse return error.Overflow, .little), .p32 => try bw.writeInt(u32, std.math.cast(u32, value) orelse return error.Overflow, .little),
.p64 => try writer.writeInt(u64, value, .little), .p64 => try bw.writeInt(u64, value, .little),
} }
} }
@ -304,8 +270,9 @@ const log = std.log.scoped(.link);
const macho = std.macho; const macho = std.macho;
const mem = std.mem; const mem = std.mem;
const std = @import("std"); const std = @import("std");
const Allocator = mem.Allocator; const Allocator = std.mem.Allocator;
const Path = std.Build.Cache.Path; const Path = std.Build.Cache.Path;
const Writer = std.io.Writer;
const Archive = @This(); const Archive = @This();
const File = @import("file.zig").File; const File = @import("file.zig").File;

View File

@ -580,8 +580,9 @@ pub fn resolveRelocs(self: Atom, macho_file: *MachO, buffer: []u8) !void {
relocs_log.debug("{x}: {s}", .{ self.value, name }); relocs_log.debug("{x}: {s}", .{ self.value, name });
var bw: Writer = .fixed(buffer);
var has_error = false; var has_error = false;
var stream = std.io.fixedBufferStream(buffer);
var i: usize = 0; var i: usize = 0;
while (i < relocs.len) : (i += 1) { while (i < relocs.len) : (i += 1) {
const rel = relocs[i]; const rel = relocs[i];
@ -592,9 +593,8 @@ pub fn resolveRelocs(self: Atom, macho_file: *MachO, buffer: []u8) !void {
if (rel.getTargetSymbol(self, macho_file).getFile(macho_file) == null) continue; if (rel.getTargetSymbol(self, macho_file).getFile(macho_file) == null) continue;
} }
try stream.seekTo(rel_offset); bw.end = std.math.cast(usize, rel_offset) orelse return error.Overflow;
self.resolveRelocInner(rel, subtractor, buffer, macho_file, stream.writer()) catch |err| { self.resolveRelocInner(rel, subtractor, buffer, macho_file, &bw) catch |err| switch (err) {
switch (err) {
error.RelaxFail => { error.RelaxFail => {
const target = switch (rel.tag) { const target = switch (rel.tag) {
.@"extern" => rel.getTargetSymbol(self, macho_file).getName(macho_file), .@"extern" => rel.getTargetSymbol(self, macho_file).getName(macho_file),
@ -602,7 +602,7 @@ pub fn resolveRelocs(self: Atom, macho_file: *MachO, buffer: []u8) !void {
}; };
try macho_file.reportParseError2( try macho_file.reportParseError2(
file.getIndex(), file.getIndex(),
"{s}: 0x{x}: 0x{x}: failed to relax relocation: type {}, target {s}", "{s}: 0x{x}: 0x{x}: failed to relax relocation: type {f}, target {s}",
.{ .{
name, name,
self.getAddress(macho_file), self.getAddress(macho_file),
@ -615,7 +615,6 @@ pub fn resolveRelocs(self: Atom, macho_file: *MachO, buffer: []u8) !void {
}, },
error.RelaxFailUnexpectedInstruction => has_error = true, error.RelaxFailUnexpectedInstruction => has_error = true,
else => |e| return e, else => |e| return e,
}
}; };
} }
@ -638,8 +637,8 @@ fn resolveRelocInner(
subtractor: ?Relocation, subtractor: ?Relocation,
code: []u8, code: []u8,
macho_file: *MachO, macho_file: *MachO,
writer: anytype, bw: *Writer,
) ResolveError!void { ) Writer.Error!void {
const t = &macho_file.base.comp.root_mod.resolved_target.result; const t = &macho_file.base.comp.root_mod.resolved_target.result;
const cpu_arch = t.cpu.arch; const cpu_arch = t.cpu.arch;
const rel_offset = math.cast(usize, rel.offset - self.off) orelse return error.Overflow; const rel_offset = math.cast(usize, rel.offset - self.off) orelse return error.Overflow;
@ -653,7 +652,7 @@ fn resolveRelocInner(
const divExact = struct { const divExact = struct {
fn divExact(atom: Atom, r: Relocation, num: u12, den: u12, ctx: *MachO) !u12 { fn divExact(atom: Atom, r: Relocation, num: u12, den: u12, ctx: *MachO) !u12 {
return math.divExact(u12, num, den) catch { return math.divExact(u12, num, den) catch {
try ctx.reportParseError2(atom.getFile(ctx).getIndex(), "{s}: unexpected remainder when resolving {s} at offset 0x{x}", .{ try ctx.reportParseError2(atom.getFile(ctx).getIndex(), "{s}: unexpected remainder when resolving {f} at offset 0x{x}", .{
atom.getName(ctx), atom.getName(ctx),
r.fmtPretty(ctx.getTarget().cpu.arch), r.fmtPretty(ctx.getTarget().cpu.arch),
r.offset, r.offset,
@ -664,14 +663,14 @@ fn resolveRelocInner(
}.divExact; }.divExact;
switch (rel.tag) { switch (rel.tag) {
.local => relocs_log.debug(" {x}<+{d}>: {}: [=> {x}] atom({d})", .{ .local => relocs_log.debug(" {x}<+{d}>: {f}: [=> {x}] atom({d})", .{
P, P,
rel_offset, rel_offset,
rel.fmtPretty(cpu_arch), rel.fmtPretty(cpu_arch),
S + A - SUB, S + A - SUB,
rel.getTargetAtom(self, macho_file).atom_index, rel.getTargetAtom(self, macho_file).atom_index,
}), }),
.@"extern" => relocs_log.debug(" {x}<+{d}>: {}: [=> {x}] G({x}) ({s})", .{ .@"extern" => relocs_log.debug(" {x}<+{d}>: {f}: [=> {x}] G({x}) ({s})", .{
P, P,
rel_offset, rel_offset,
rel.fmtPretty(cpu_arch), rel.fmtPretty(cpu_arch),
@ -690,14 +689,14 @@ fn resolveRelocInner(
if (rel.tag == .@"extern") { if (rel.tag == .@"extern") {
const sym = rel.getTargetSymbol(self, macho_file); const sym = rel.getTargetSymbol(self, macho_file);
if (sym.isTlvInit(macho_file)) { if (sym.isTlvInit(macho_file)) {
try writer.writeInt(u64, @intCast(S - TLS), .little); try bw.writeInt(u64, @intCast(S - TLS), .little);
return; return;
} }
if (sym.flags.import) return; if (sym.flags.import) return;
} }
try writer.writeInt(u64, @bitCast(S + A - SUB), .little); try bw.writeInt(u64, @bitCast(S + A - SUB), .little);
} else if (rel.meta.length == 2) { } else if (rel.meta.length == 2) {
try writer.writeInt(u32, @bitCast(@as(i32, @truncate(S + A - SUB))), .little); try bw.writeInt(u32, @bitCast(@as(i32, @truncate(S + A - SUB))), .little);
} else unreachable; } else unreachable;
}, },
@ -705,7 +704,7 @@ fn resolveRelocInner(
assert(rel.tag == .@"extern"); assert(rel.tag == .@"extern");
assert(rel.meta.length == 2); assert(rel.meta.length == 2);
assert(rel.meta.pcrel); assert(rel.meta.pcrel);
try writer.writeInt(i32, @intCast(G + A - P), .little); try bw.writeInt(i32, @intCast(G + A - P), .little);
}, },
.branch => { .branch => {
@ -714,7 +713,7 @@ fn resolveRelocInner(
assert(rel.tag == .@"extern"); assert(rel.tag == .@"extern");
switch (cpu_arch) { switch (cpu_arch) {
.x86_64 => try writer.writeInt(i32, @intCast(S + A - P), .little), .x86_64 => try bw.writeInt(i32, @intCast(S + A - P), .little),
.aarch64 => { .aarch64 => {
const disp: i28 = math.cast(i28, S + A - P) orelse blk: { const disp: i28 = math.cast(i28, S + A - P) orelse blk: {
const thunk = self.getThunk(macho_file); const thunk = self.getThunk(macho_file);
@ -732,10 +731,10 @@ fn resolveRelocInner(
assert(rel.meta.length == 2); assert(rel.meta.length == 2);
assert(rel.meta.pcrel); assert(rel.meta.pcrel);
if (rel.getTargetSymbol(self, macho_file).getSectionFlags().has_got) { if (rel.getTargetSymbol(self, macho_file).getSectionFlags().has_got) {
try writer.writeInt(i32, @intCast(G + A - P), .little); try bw.writeInt(i32, @intCast(G + A - P), .little);
} else { } else {
try x86_64.relaxGotLoad(self, code[rel_offset - 3 ..], rel, macho_file); try x86_64.relaxGotLoad(self, code[rel_offset - 3 ..], rel, macho_file);
try writer.writeInt(i32, @intCast(S + A - P), .little); try bw.writeInt(i32, @intCast(S + A - P), .little);
} }
}, },
@ -746,17 +745,17 @@ fn resolveRelocInner(
const sym = rel.getTargetSymbol(self, macho_file); const sym = rel.getTargetSymbol(self, macho_file);
if (sym.getSectionFlags().tlv_ptr) { if (sym.getSectionFlags().tlv_ptr) {
const S_: i64 = @intCast(sym.getTlvPtrAddress(macho_file)); const S_: i64 = @intCast(sym.getTlvPtrAddress(macho_file));
try writer.writeInt(i32, @intCast(S_ + A - P), .little); try bw.writeInt(i32, @intCast(S_ + A - P), .little);
} else { } else {
try x86_64.relaxTlv(code[rel_offset - 3 ..], t); try x86_64.relaxTlv(code[rel_offset - 3 ..], t);
try writer.writeInt(i32, @intCast(S + A - P), .little); try bw.writeInt(i32, @intCast(S + A - P), .little);
} }
}, },
.signed, .signed1, .signed2, .signed4 => { .signed, .signed1, .signed2, .signed4 => {
assert(rel.meta.length == 2); assert(rel.meta.length == 2);
assert(rel.meta.pcrel); assert(rel.meta.pcrel);
try writer.writeInt(i32, @intCast(S + A - P), .little); try bw.writeInt(i32, @intCast(S + A - P), .little);
}, },
.page, .page,
@ -808,7 +807,7 @@ fn resolveRelocInner(
2 => try divExact(self, rel, @truncate(target), 4, macho_file), 2 => try divExact(self, rel, @truncate(target), 4, macho_file),
3 => try divExact(self, rel, @truncate(target), 8, macho_file), 3 => try divExact(self, rel, @truncate(target), 8, macho_file),
}; };
try writer.writeInt(u32, inst.toU32(), .little); try bw.writeInt(u32, inst.toU32(), .little);
} }
}, },
@ -886,7 +885,7 @@ fn resolveRelocInner(
.sf = @as(u1, @truncate(reg_info.size)), .sf = @as(u1, @truncate(reg_info.size)),
}, },
}; };
try writer.writeInt(u32, inst.toU32(), .little); try bw.writeInt(u32, inst.toU32(), .little);
}, },
} }
} }
@ -900,19 +899,19 @@ const x86_64 = struct {
switch (old_inst.encoding.mnemonic) { switch (old_inst.encoding.mnemonic) {
.mov => { .mov => {
const inst = Instruction.new(old_inst.prefix, .lea, &old_inst.ops, t) catch return error.RelaxFail; const inst = Instruction.new(old_inst.prefix, .lea, &old_inst.ops, t) catch return error.RelaxFail;
relocs_log.debug(" relaxing {} => {}", .{ old_inst.encoding, inst.encoding }); relocs_log.debug(" relaxing {f} => {f}", .{ old_inst.encoding, inst.encoding });
encode(&.{inst}, code) catch return error.RelaxFail; encode(&.{inst}, code) catch return error.RelaxFail;
}, },
else => |x| { else => |x| {
var err = try diags.addErrorWithNotes(2); var err = try diags.addErrorWithNotes(2);
try err.addMsg("{s}: 0x{x}: 0x{x}: failed to relax relocation of type {}", .{ try err.addMsg("{s}: 0x{x}: 0x{x}: failed to relax relocation of type {f}", .{
self.getName(macho_file), self.getName(macho_file),
self.getAddress(macho_file), self.getAddress(macho_file),
rel.offset, rel.offset,
rel.fmtPretty(.x86_64), rel.fmtPretty(.x86_64),
}); });
err.addNote("expected .mov instruction but found .{s}", .{@tagName(x)}); err.addNote("expected .mov instruction but found .{s}", .{@tagName(x)});
err.addNote("while parsing {}", .{self.getFile(macho_file).fmtPath()}); err.addNote("while parsing {f}", .{self.getFile(macho_file).fmtPath()});
return error.RelaxFailUnexpectedInstruction; return error.RelaxFailUnexpectedInstruction;
}, },
} }
@ -924,7 +923,7 @@ const x86_64 = struct {
switch (old_inst.encoding.mnemonic) { switch (old_inst.encoding.mnemonic) {
.mov => { .mov => {
const inst = Instruction.new(old_inst.prefix, .lea, &old_inst.ops, t) catch return error.RelaxFail; const inst = Instruction.new(old_inst.prefix, .lea, &old_inst.ops, t) catch return error.RelaxFail;
relocs_log.debug(" relaxing {} => {}", .{ old_inst.encoding, inst.encoding }); relocs_log.debug(" relaxing {f} => {f}", .{ old_inst.encoding, inst.encoding });
encode(&.{inst}, code) catch return error.RelaxFail; encode(&.{inst}, code) catch return error.RelaxFail;
}, },
else => return error.RelaxFail, else => return error.RelaxFail,
@ -938,11 +937,8 @@ const x86_64 = struct {
} }
fn encode(insts: []const Instruction, code: []u8) !void { fn encode(insts: []const Instruction, code: []u8) !void {
var stream = std.io.fixedBufferStream(code); var bw: Writer = .fixed(code);
const writer = stream.writer(); for (insts) |inst| try inst.encode(&bw, .{});
for (insts) |inst| {
try inst.encode(writer, .{});
}
} }
const bits = @import("../../arch/x86_64/bits.zig"); const bits = @import("../../arch/x86_64/bits.zig");
@ -1003,7 +999,7 @@ pub fn writeRelocs(self: Atom, macho_file: *MachO, code: []u8, buffer: []macho.r
} }
switch (rel.tag) { switch (rel.tag) {
.local => relocs_log.debug(" {}: [{x} => {d}({s},{s})] + {x}", .{ .local => relocs_log.debug(" {f}: [{x} => {d}({s},{s})] + {x}", .{
rel.fmtPretty(cpu_arch), rel.fmtPretty(cpu_arch),
r_address, r_address,
r_symbolnum, r_symbolnum,
@ -1011,7 +1007,7 @@ pub fn writeRelocs(self: Atom, macho_file: *MachO, code: []u8, buffer: []macho.r
macho_file.sections.items(.header)[r_symbolnum - 1].sectName(), macho_file.sections.items(.header)[r_symbolnum - 1].sectName(),
addend, addend,
}), }),
.@"extern" => relocs_log.debug(" {}: [{x} => {d}({s})] + {x}", .{ .@"extern" => relocs_log.debug(" {f}: [{x} => {d}({s})] + {x}", .{
rel.fmtPretty(cpu_arch), rel.fmtPretty(cpu_arch),
r_address, r_address,
r_symbolnum, r_symbolnum,
@ -1117,60 +1113,40 @@ pub fn writeRelocs(self: Atom, macho_file: *MachO, code: []u8, buffer: []macho.r
assert(i == buffer.len); assert(i == buffer.len);
} }
pub fn format( pub fn fmt(atom: Atom, macho_file: *MachO) std.fmt.Formatter(Format, Format.print) {
atom: Atom,
comptime unused_fmt_string: []const u8,
options: std.fmt.FormatOptions,
writer: anytype,
) !void {
_ = atom;
_ = unused_fmt_string;
_ = options;
_ = writer;
@compileError("do not format Atom directly");
}
pub fn fmt(atom: Atom, macho_file: *MachO) std.fmt.Formatter(format2) {
return .{ .data = .{ return .{ .data = .{
.atom = atom, .atom = atom,
.macho_file = macho_file, .macho_file = macho_file,
} }; } };
} }
const FormatContext = struct { const Format = struct {
atom: Atom, atom: Atom,
macho_file: *MachO, macho_file: *MachO,
};
fn format2( fn print(f: Format, w: *Writer) Writer.Error!void {
ctx: FormatContext, const atom = f.atom;
comptime unused_fmt_string: []const u8, const macho_file = f.macho_file;
options: std.fmt.FormatOptions,
writer: anytype,
) !void {
_ = options;
_ = unused_fmt_string;
const atom = ctx.atom;
const macho_file = ctx.macho_file;
const file = atom.getFile(macho_file); const file = atom.getFile(macho_file);
try writer.print("atom({d}) : {s} : @{x} : sect({d}) : align({x}) : size({x}) : nreloc({d}) : thunk({d})", .{ try w.print("atom({d}) : {s} : @{x} : sect({d}) : align({x}) : size({x}) : nreloc({d}) : thunk({d})", .{
atom.atom_index, atom.getName(macho_file), atom.getAddress(macho_file), atom.atom_index, atom.getName(macho_file), atom.getAddress(macho_file),
atom.out_n_sect, atom.alignment, atom.size, atom.out_n_sect, atom.alignment, atom.size,
atom.getRelocs(macho_file).len, atom.getExtra(macho_file).thunk, atom.getRelocs(macho_file).len, atom.getExtra(macho_file).thunk,
}); });
if (!atom.isAlive()) try writer.writeAll(" : [*]"); if (!atom.isAlive()) try w.writeAll(" : [*]");
if (atom.getUnwindRecords(macho_file).len > 0) { if (atom.getUnwindRecords(macho_file).len > 0) {
try writer.writeAll(" : unwind{ "); try w.writeAll(" : unwind{ ");
const extra = atom.getExtra(macho_file); const extra = atom.getExtra(macho_file);
for (atom.getUnwindRecords(macho_file), extra.unwind_index..) |index, i| { for (atom.getUnwindRecords(macho_file), extra.unwind_index..) |index, i| {
const rec = file.object.getUnwindRecord(index); const rec = file.object.getUnwindRecord(index);
try writer.print("{d}", .{index}); try w.print("{d}", .{index});
if (!rec.alive) try writer.writeAll("([*])"); if (!rec.alive) try w.writeAll("([*])");
if (i < extra.unwind_index + extra.unwind_count - 1) try writer.writeAll(", "); if (i < extra.unwind_index + extra.unwind_count - 1) try w.writeAll(", ");
} }
try writer.writeAll(" }"); try w.writeAll(" }");
} }
} }
};
pub const Index = u32; pub const Index = u32;
@ -1205,19 +1181,20 @@ pub const Extra = struct {
pub const Alignment = @import("../../InternPool.zig").Alignment; pub const Alignment = @import("../../InternPool.zig").Alignment;
const aarch64 = @import("../aarch64.zig"); const std = @import("std");
const assert = std.debug.assert; const assert = std.debug.assert;
const macho = std.macho; const macho = std.macho;
const math = std.math; const math = std.math;
const mem = std.mem; const mem = std.mem;
const log = std.log.scoped(.link); const log = std.log.scoped(.link);
const relocs_log = std.log.scoped(.link_relocs); const relocs_log = std.log.scoped(.link_relocs);
const std = @import("std"); const Writer = std.io.Writer;
const trace = @import("../../tracy.zig").trace;
const Allocator = mem.Allocator; const Allocator = mem.Allocator;
const Atom = @This();
const AtomicBool = std.atomic.Value(bool); const AtomicBool = std.atomic.Value(bool);
const aarch64 = @import("../aarch64.zig");
const trace = @import("../../tracy.zig").trace;
const Atom = @This();
const File = @import("file.zig").File; const File = @import("file.zig").File;
const MachO = @import("../MachO.zig"); const MachO = @import("../MachO.zig");
const Object = @import("Object.zig"); const Object = @import("Object.zig");

View File

@ -247,7 +247,7 @@ pub fn deinit(self: *CodeSignature, allocator: Allocator) void {
pub fn addEntitlements(self: *CodeSignature, allocator: Allocator, path: []const u8) !void { pub fn addEntitlements(self: *CodeSignature, allocator: Allocator, path: []const u8) !void {
const file = try fs.cwd().openFile(path, .{}); const file = try fs.cwd().openFile(path, .{});
defer file.close(); defer file.close();
const inner = try file.readToEndAlloc(allocator, std.math.maxInt(u32)); const inner = try file.readToEndAlloc(allocator, .unlimited);
self.entitlements = .{ .inner = inner }; self.entitlements = .{ .inner = inner };
} }
@ -304,10 +304,11 @@ pub fn writeAdhocSignature(
var hash: [hash_size]u8 = undefined; var hash: [hash_size]u8 = undefined;
if (self.requirements) |*req| { if (self.requirements) |*req| {
var buf = std.ArrayList(u8).init(allocator); var aw: std.io.Writer.Allocating = .init(allocator);
defer buf.deinit(); defer aw.deinit();
try req.write(buf.writer());
Sha256.hash(buf.items, &hash, .{}); try req.write(&aw.writer);
Sha256.hash(aw.getWritten(), &hash, .{});
self.code_directory.addSpecialHash(req.slotType(), hash); self.code_directory.addSpecialHash(req.slotType(), hash);
try blobs.append(.{ .requirements = req }); try blobs.append(.{ .requirements = req });
@ -316,10 +317,11 @@ pub fn writeAdhocSignature(
} }
if (self.entitlements) |*ents| { if (self.entitlements) |*ents| {
var buf = std.ArrayList(u8).init(allocator); var aw: std.io.Writer.Allocating = .init(allocator);
defer buf.deinit(); defer aw.deinit();
try ents.write(buf.writer());
Sha256.hash(buf.items, &hash, .{}); try ents.write(&aw.writer);
Sha256.hash(aw.getWritten(), &hash, .{});
self.code_directory.addSpecialHash(ents.slotType(), hash); self.code_directory.addSpecialHash(ents.slotType(), hash);
try blobs.append(.{ .entitlements = ents }); try blobs.append(.{ .entitlements = ents });

View File

@ -269,18 +269,14 @@ fn finalizeDwarfSegment(self: *DebugSymbols, macho_file: *MachO) void {
fn writeLoadCommands(self: *DebugSymbols, macho_file: *MachO) !struct { usize, usize } { fn writeLoadCommands(self: *DebugSymbols, macho_file: *MachO) !struct { usize, usize } {
const gpa = self.allocator; const gpa = self.allocator;
const needed_size = load_commands.calcLoadCommandsSizeDsym(macho_file, self); var bw: Writer = .fixed(try gpa.alloc(u8, load_commands.calcLoadCommandsSizeDsym(macho_file, self)));
const buffer = try gpa.alloc(u8, needed_size); defer gpa.free(bw.buffer);
defer gpa.free(buffer);
var stream = std.io.fixedBufferStream(buffer);
const writer = stream.writer();
var ncmds: usize = 0; var ncmds: usize = 0;
// UUID comes first presumably to speed up lookup by the consumer like lldb. // UUID comes first presumably to speed up lookup by the consumer like lldb.
@memcpy(&self.uuid_cmd.uuid, &macho_file.uuid_cmd.uuid); @memcpy(&self.uuid_cmd.uuid, &macho_file.uuid_cmd.uuid);
try writer.writeStruct(self.uuid_cmd); try bw.writeStruct(self.uuid_cmd);
ncmds += 1; ncmds += 1;
// Segment and section load commands // Segment and section load commands
@ -293,11 +289,11 @@ fn writeLoadCommands(self: *DebugSymbols, macho_file: *MachO) !struct { usize, u
var out_seg = seg; var out_seg = seg;
out_seg.fileoff = 0; out_seg.fileoff = 0;
out_seg.filesize = 0; out_seg.filesize = 0;
try writer.writeStruct(out_seg); try bw.writeStruct(out_seg);
for (slice.items(.header)[sect_id..][0..seg.nsects]) |header| { for (slice.items(.header)[sect_id..][0..seg.nsects]) |header| {
var out_header = header; var out_header = header;
out_header.offset = 0; out_header.offset = 0;
try writer.writeStruct(out_header); try bw.writeStruct(out_header);
} }
sect_id += seg.nsects; sect_id += seg.nsects;
} }
@ -306,23 +302,22 @@ fn writeLoadCommands(self: *DebugSymbols, macho_file: *MachO) !struct { usize, u
// Next, commit DSYM's __LINKEDIT and __DWARF segments headers. // Next, commit DSYM's __LINKEDIT and __DWARF segments headers.
sect_id = 0; sect_id = 0;
for (self.segments.items) |seg| { for (self.segments.items) |seg| {
try writer.writeStruct(seg); try bw.writeStruct(seg);
for (self.sections.items[sect_id..][0..seg.nsects]) |header| { for (self.sections.items[sect_id..][0..seg.nsects]) |header| {
try writer.writeStruct(header); try bw.writeStruct(header);
} }
sect_id += seg.nsects; sect_id += seg.nsects;
} }
ncmds += self.segments.items.len; ncmds += self.segments.items.len;
} }
try writer.writeStruct(self.symtab_cmd); try bw.writeStruct(self.symtab_cmd);
ncmds += 1; ncmds += 1;
assert(stream.pos == needed_size); assert(bw.end == bw.buffer.len);
try self.file.?.pwriteAll(bw.buffer, @sizeOf(macho.mach_header_64));
try self.file.?.pwriteAll(buffer, @sizeOf(macho.mach_header_64)); return .{ ncmds, bw.end };
return .{ ncmds, buffer.len };
} }
fn writeHeader(self: *DebugSymbols, macho_file: *MachO, ncmds: usize, sizeofcmds: usize) !void { fn writeHeader(self: *DebugSymbols, macho_file: *MachO, ncmds: usize, sizeofcmds: usize) !void {
@ -460,6 +455,7 @@ const math = std.math;
const mem = std.mem; const mem = std.mem;
const padToIdeal = MachO.padToIdeal; const padToIdeal = MachO.padToIdeal;
const trace = @import("../../tracy.zig").trace; const trace = @import("../../tracy.zig").trace;
const Writer = std.io.Writer;
const Allocator = mem.Allocator; const Allocator = mem.Allocator;
const MachO = @import("../MachO.zig"); const MachO = @import("../MachO.zig");

View File

@ -81,7 +81,7 @@ pub const InfoReader = struct {
.dwarf64 => 12, .dwarf64 => 12,
} + cuh_length; } + cuh_length;
while (p.pos < end_pos) { while (p.pos < end_pos) {
const di_code = try p.readUleb128(u64); const di_code = try p.readLeb128(u64);
if (di_code == 0) return error.UnexpectedEndOfFile; if (di_code == 0) return error.UnexpectedEndOfFile;
if (di_code == code) return; if (di_code == code) return;
@ -174,14 +174,14 @@ pub const InfoReader = struct {
dw.FORM.block1 => try p.readByte(), dw.FORM.block1 => try p.readByte(),
dw.FORM.block2 => try p.readInt(u16), dw.FORM.block2 => try p.readInt(u16),
dw.FORM.block4 => try p.readInt(u32), dw.FORM.block4 => try p.readInt(u32),
dw.FORM.block => try p.readUleb128(u64), dw.FORM.block => try p.readLeb128(u64),
else => unreachable, else => unreachable,
}; };
return p.readNBytes(len); return p.readNBytes(len);
} }
pub fn readExprLoc(p: *InfoReader) ![]const u8 { pub fn readExprLoc(p: *InfoReader) ![]const u8 {
const len: u64 = try p.readUleb128(u64); const len: u64 = try p.readLeb128(u64);
return p.readNBytes(len); return p.readNBytes(len);
} }
@ -191,8 +191,8 @@ pub const InfoReader = struct {
dw.FORM.data2, dw.FORM.ref2 => try p.readInt(u16), dw.FORM.data2, dw.FORM.ref2 => try p.readInt(u16),
dw.FORM.data4, dw.FORM.ref4 => try p.readInt(u32), dw.FORM.data4, dw.FORM.ref4 => try p.readInt(u32),
dw.FORM.data8, dw.FORM.ref8, dw.FORM.ref_sig8 => try p.readInt(u64), dw.FORM.data8, dw.FORM.ref8, dw.FORM.ref_sig8 => try p.readInt(u64),
dw.FORM.udata, dw.FORM.ref_udata => try p.readUleb128(u64), dw.FORM.udata, dw.FORM.ref_udata => try p.readLeb128(u64),
dw.FORM.sdata => @bitCast(try p.readIleb128(i64)), dw.FORM.sdata => @bitCast(try p.readLeb128(i64)),
else => return error.UnhandledConstantForm, else => return error.UnhandledConstantForm,
}; };
} }
@ -203,7 +203,7 @@ pub const InfoReader = struct {
dw.FORM.strx2, dw.FORM.addrx2 => try p.readInt(u16), dw.FORM.strx2, dw.FORM.addrx2 => try p.readInt(u16),
dw.FORM.strx3, dw.FORM.addrx3 => error.UnhandledForm, dw.FORM.strx3, dw.FORM.addrx3 => error.UnhandledForm,
dw.FORM.strx4, dw.FORM.addrx4 => try p.readInt(u32), dw.FORM.strx4, dw.FORM.addrx4 => try p.readInt(u32),
dw.FORM.strx, dw.FORM.addrx => try p.readUleb128(u64), dw.FORM.strx, dw.FORM.addrx => try p.readLeb128(u64),
else => return error.UnhandledIndexForm, else => return error.UnhandledIndexForm,
}; };
} }
@ -272,20 +272,10 @@ pub const InfoReader = struct {
}; };
} }
pub fn readUleb128(p: *InfoReader, comptime Type: type) !Type { pub fn readLeb128(p: *InfoReader, comptime Type: type) !Type {
var stream = std.io.fixedBufferStream(p.bytes()[p.pos..]); var r: std.io.Reader = .fixed(p.bytes()[p.pos..]);
var creader = std.io.countingReader(stream.reader()); defer p.pos += r.seek;
const value: Type = try leb.readUleb128(Type, creader.reader()); return r.takeLeb128(Type);
p.pos += math.cast(usize, creader.bytes_read) orelse return error.Overflow;
return value;
}
pub fn readIleb128(p: *InfoReader, comptime Type: type) !Type {
var stream = std.io.fixedBufferStream(p.bytes()[p.pos..]);
var creader = std.io.countingReader(stream.reader());
const value: Type = try leb.readIleb128(Type, creader.reader());
p.pos += math.cast(usize, creader.bytes_read) orelse return error.Overflow;
return value;
} }
pub fn seekTo(p: *InfoReader, off: u64) !void { pub fn seekTo(p: *InfoReader, off: u64) !void {
@ -307,10 +297,10 @@ pub const AbbrevReader = struct {
pub fn readDecl(p: *AbbrevReader) !?AbbrevDecl { pub fn readDecl(p: *AbbrevReader) !?AbbrevDecl {
const pos = p.pos; const pos = p.pos;
const code = try p.readUleb128(Code); const code = try p.readLeb128(Code);
if (code == 0) return null; if (code == 0) return null;
const tag = try p.readUleb128(Tag); const tag = try p.readLeb128(Tag);
const has_children = (try p.readByte()) > 0; const has_children = (try p.readByte()) > 0;
return .{ return .{
.code = code, .code = code,
@ -323,8 +313,8 @@ pub const AbbrevReader = struct {
pub fn readAttr(p: *AbbrevReader) !?AbbrevAttr { pub fn readAttr(p: *AbbrevReader) !?AbbrevAttr {
const pos = p.pos; const pos = p.pos;
const at = try p.readUleb128(At); const at = try p.readLeb128(At);
const form = try p.readUleb128(Form); const form = try p.readLeb128(Form);
return if (at == 0 and form == 0) null else .{ return if (at == 0 and form == 0) null else .{
.at = at, .at = at,
.form = form, .form = form,
@ -339,12 +329,10 @@ pub const AbbrevReader = struct {
return p.bytes()[p.pos]; return p.bytes()[p.pos];
} }
pub fn readUleb128(p: *AbbrevReader, comptime Type: type) !Type { pub fn readLeb128(p: *AbbrevReader, comptime Type: type) !Type {
var stream = std.io.fixedBufferStream(p.bytes()[p.pos..]); var r: std.io.Reader = .fixed(p.bytes()[p.pos..]);
var creader = std.io.countingReader(stream.reader()); defer p.pos += r.seek;
const value: Type = try leb.readUleb128(Type, creader.reader()); return r.takeLeb128(Type);
p.pos += math.cast(usize, creader.bytes_read) orelse return error.Overflow;
return value;
} }
pub fn seekTo(p: *AbbrevReader, off: u64) !void { pub fn seekTo(p: *AbbrevReader, off: u64) !void {

View File

@ -61,7 +61,7 @@ fn parseBinary(self: *Dylib, macho_file: *MachO) !void {
const file = macho_file.getFileHandle(self.file_handle); const file = macho_file.getFileHandle(self.file_handle);
const offset = self.offset; const offset = self.offset;
log.debug("parsing dylib from binary: {}", .{@as(Path, self.path)}); log.debug("parsing dylib from binary: {f}", .{@as(Path, self.path)});
var header_buffer: [@sizeOf(macho.mach_header_64)]u8 = undefined; var header_buffer: [@sizeOf(macho.mach_header_64)]u8 = undefined;
{ {
@ -140,7 +140,7 @@ fn parseBinary(self: *Dylib, macho_file: *MachO) !void {
if (self.platform) |platform| { if (self.platform) |platform| {
if (!macho_file.platform.eqlTarget(platform)) { if (!macho_file.platform.eqlTarget(platform)) {
try macho_file.reportParseError2(self.index, "invalid platform: {}", .{ try macho_file.reportParseError2(self.index, "invalid platform: {f}", .{
platform.fmtTarget(macho_file.getTarget().cpu.arch), platform.fmtTarget(macho_file.getTarget().cpu.arch),
}); });
return error.InvalidTarget; return error.InvalidTarget;
@ -148,7 +148,7 @@ fn parseBinary(self: *Dylib, macho_file: *MachO) !void {
// TODO: this can cause the CI to fail so I'm commenting this check out so that // TODO: this can cause the CI to fail so I'm commenting this check out so that
// I can work out the rest of the changes first // I can work out the rest of the changes first
// if (macho_file.platform.version.order(platform.version) == .lt) { // if (macho_file.platform.version.order(platform.version) == .lt) {
// try macho_file.reportParseError2(self.index, "object file built for newer platform: {}: {} < {}", .{ // try macho_file.reportParseError2(self.index, "object file built for newer platform: {f}: {f} < {f}", .{
// macho_file.platform.fmtTarget(macho_file.getTarget().cpu.arch), // macho_file.platform.fmtTarget(macho_file.getTarget().cpu.arch),
// macho_file.platform.version, // macho_file.platform.version,
// platform.version, // platform.version,
@ -158,46 +158,6 @@ fn parseBinary(self: *Dylib, macho_file: *MachO) !void {
} }
} }
const TrieIterator = struct {
data: []const u8,
pos: usize = 0,
fn getStream(it: *TrieIterator) std.io.FixedBufferStream([]const u8) {
return std.io.fixedBufferStream(it.data[it.pos..]);
}
fn readUleb128(it: *TrieIterator) !u64 {
var stream = it.getStream();
var creader = std.io.countingReader(stream.reader());
const reader = creader.reader();
const value = try std.leb.readUleb128(u64, reader);
it.pos += math.cast(usize, creader.bytes_read) orelse return error.Overflow;
return value;
}
fn readString(it: *TrieIterator) ![:0]const u8 {
var stream = it.getStream();
const reader = stream.reader();
var count: usize = 0;
while (true) : (count += 1) {
const byte = try reader.readByte();
if (byte == 0) break;
}
const str = @as([*:0]const u8, @ptrCast(it.data.ptr + it.pos))[0..count :0];
it.pos += count + 1;
return str;
}
fn readByte(it: *TrieIterator) !u8 {
var stream = it.getStream();
const value = try stream.reader().readByte();
it.pos += 1;
return value;
}
};
pub fn addExport(self: *Dylib, allocator: Allocator, name: []const u8, flags: Export.Flags) !void { pub fn addExport(self: *Dylib, allocator: Allocator, name: []const u8, flags: Export.Flags) !void {
try self.exports.append(allocator, .{ try self.exports.append(allocator, .{
.name = try self.addString(allocator, name), .name = try self.addString(allocator, name),
@ -207,16 +167,16 @@ pub fn addExport(self: *Dylib, allocator: Allocator, name: []const u8, flags: Ex
fn parseTrieNode( fn parseTrieNode(
self: *Dylib, self: *Dylib,
it: *TrieIterator, br: *std.io.Reader,
allocator: Allocator, allocator: Allocator,
arena: Allocator, arena: Allocator,
prefix: []const u8, prefix: []const u8,
) !void { ) !void {
const tracy = trace(@src()); const tracy = trace(@src());
defer tracy.end(); defer tracy.end();
const size = try it.readUleb128(); const size = try br.takeLeb128(u64);
if (size > 0) { if (size > 0) {
const flags = try it.readUleb128(); const flags = try br.takeLeb128(u8);
const kind = flags & macho.EXPORT_SYMBOL_FLAGS_KIND_MASK; const kind = flags & macho.EXPORT_SYMBOL_FLAGS_KIND_MASK;
const out_flags = Export.Flags{ const out_flags = Export.Flags{
.abs = kind == macho.EXPORT_SYMBOL_FLAGS_KIND_ABSOLUTE, .abs = kind == macho.EXPORT_SYMBOL_FLAGS_KIND_ABSOLUTE,
@ -224,29 +184,28 @@ fn parseTrieNode(
.weak = flags & macho.EXPORT_SYMBOL_FLAGS_WEAK_DEFINITION != 0, .weak = flags & macho.EXPORT_SYMBOL_FLAGS_WEAK_DEFINITION != 0,
}; };
if (flags & macho.EXPORT_SYMBOL_FLAGS_REEXPORT != 0) { if (flags & macho.EXPORT_SYMBOL_FLAGS_REEXPORT != 0) {
_ = try it.readUleb128(); // dylib ordinal _ = try br.takeLeb128(u64); // dylib ordinal
const name = try it.readString(); const name = try br.takeSentinel(0);
try self.addExport(allocator, if (name.len > 0) name else prefix, out_flags); try self.addExport(allocator, if (name.len > 0) name else prefix, out_flags);
} else if (flags & macho.EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER != 0) { } else if (flags & macho.EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER != 0) {
_ = try it.readUleb128(); // stub offset _ = try br.takeLeb128(u64); // stub offset
_ = try it.readUleb128(); // resolver offset _ = try br.takeLeb128(u64); // resolver offset
try self.addExport(allocator, prefix, out_flags); try self.addExport(allocator, prefix, out_flags);
} else { } else {
_ = try it.readUleb128(); // VM offset _ = try br.takeLeb128(u64); // VM offset
try self.addExport(allocator, prefix, out_flags); try self.addExport(allocator, prefix, out_flags);
} }
} }
const nedges = try it.readByte(); const nedges = try br.takeByte();
for (0..nedges) |_| { for (0..nedges) |_| {
const label = try it.readString(); const label = try br.takeSentinel(0);
const off = try it.readUleb128(); const off = try br.takeLeb128(usize);
const prefix_label = try std.fmt.allocPrint(arena, "{s}{s}", .{ prefix, label }); const prefix_label = try std.fmt.allocPrint(arena, "{s}{s}", .{ prefix, label });
const curr = it.pos; const seek = br.seek;
it.pos = math.cast(usize, off) orelse return error.Overflow; br.seek = off;
try self.parseTrieNode(it, allocator, arena, prefix_label); try self.parseTrieNode(br, allocator, arena, prefix_label);
it.pos = curr; br.seek = seek;
} }
} }
@ -257,8 +216,8 @@ fn parseTrie(self: *Dylib, data: []const u8, macho_file: *MachO) !void {
var arena = std.heap.ArenaAllocator.init(gpa); var arena = std.heap.ArenaAllocator.init(gpa);
defer arena.deinit(); defer arena.deinit();
var it: TrieIterator = .{ .data = data }; var r: std.io.Reader = .fixed(data);
try self.parseTrieNode(&it, gpa, arena.allocator(), ""); try self.parseTrieNode(&r, gpa, arena.allocator(), "");
} }
fn parseTbd(self: *Dylib, macho_file: *MachO) !void { fn parseTbd(self: *Dylib, macho_file: *MachO) !void {
@ -267,7 +226,7 @@ fn parseTbd(self: *Dylib, macho_file: *MachO) !void {
const gpa = macho_file.base.comp.gpa; const gpa = macho_file.base.comp.gpa;
log.debug("parsing dylib from stub: {}", .{self.path}); log.debug("parsing dylib from stub: {f}", .{self.path});
const file = macho_file.getFileHandle(self.file_handle); const file = macho_file.getFileHandle(self.file_handle);
var lib_stub = LibStub.loadFromFile(gpa, file) catch |err| { var lib_stub = LibStub.loadFromFile(gpa, file) catch |err| {
@ -691,52 +650,32 @@ pub fn setSymbolExtra(self: *Dylib, index: u32, extra: Symbol.Extra) void {
} }
} }
pub fn format( pub fn fmtSymtab(self: *Dylib, macho_file: *MachO) std.fmt.Formatter(Format, Format.symtab) {
self: *Dylib,
comptime unused_fmt_string: []const u8,
options: std.fmt.FormatOptions,
writer: anytype,
) !void {
_ = self;
_ = unused_fmt_string;
_ = options;
_ = writer;
@compileError("do not format dylib directly");
}
pub fn fmtSymtab(self: *Dylib, macho_file: *MachO) std.fmt.Formatter(formatSymtab) {
return .{ .data = .{ return .{ .data = .{
.dylib = self, .dylib = self,
.macho_file = macho_file, .macho_file = macho_file,
} }; } };
} }
const FormatContext = struct { const Format = struct {
dylib: *Dylib, dylib: *Dylib,
macho_file: *MachO, macho_file: *MachO,
};
fn formatSymtab( fn symtab(f: Format, w: *Writer) Writer.Error!void {
ctx: FormatContext, const dylib = f.dylib;
comptime unused_fmt_string: []const u8, const macho_file = f.macho_file;
options: std.fmt.FormatOptions, try w.writeAll(" globals\n");
writer: anytype,
) !void {
_ = unused_fmt_string;
_ = options;
const dylib = ctx.dylib;
const macho_file = ctx.macho_file;
try writer.writeAll(" globals\n");
for (dylib.symbols.items, 0..) |sym, i| { for (dylib.symbols.items, 0..) |sym, i| {
const ref = dylib.getSymbolRef(@intCast(i), macho_file); const ref = dylib.getSymbolRef(@intCast(i), macho_file);
if (ref.getFile(macho_file) == null) { if (ref.getFile(macho_file) == null) {
// TODO any better way of handling this? // TODO any better way of handling this?
try writer.print(" {s} : unclaimed\n", .{sym.getName(macho_file)}); try w.print(" {s} : unclaimed\n", .{sym.getName(macho_file)});
} else { } else {
try writer.print(" {}\n", .{ref.getSymbol(macho_file).?.fmt(macho_file)}); try w.print(" {f}\n", .{ref.getSymbol(macho_file).?.fmt(macho_file)});
} }
} }
} }
};
pub const TargetMatcher = struct { pub const TargetMatcher = struct {
allocator: Allocator, allocator: Allocator,
@ -948,19 +887,17 @@ const Export = struct {
}; };
}; };
const std = @import("std");
const assert = std.debug.assert; const assert = std.debug.assert;
const fat = @import("fat.zig");
const fs = std.fs; const fs = std.fs;
const fmt = std.fmt; const fmt = std.fmt;
const log = std.log.scoped(.link); const log = std.log.scoped(.link);
const macho = std.macho; const macho = std.macho;
const math = std.math; const math = std.math;
const mem = std.mem; const mem = std.mem;
const tapi = @import("../tapi.zig");
const trace = @import("../../tracy.zig").trace;
const std = @import("std");
const Allocator = mem.Allocator; const Allocator = mem.Allocator;
const Path = std.Build.Cache.Path; const Path = std.Build.Cache.Path;
const Writer = std.io.Writer;
const Dylib = @This(); const Dylib = @This();
const File = @import("file.zig").File; const File = @import("file.zig").File;
@ -969,3 +906,6 @@ const LoadCommandIterator = macho.LoadCommandIterator;
const MachO = @import("../MachO.zig"); const MachO = @import("../MachO.zig");
const Symbol = @import("Symbol.zig"); const Symbol = @import("Symbol.zig");
const Tbd = tapi.Tbd; const Tbd = tapi.Tbd;
const fat = @import("fat.zig");
const tapi = @import("../tapi.zig");
const trace = @import("../../tracy.zig").trace;

View File

@ -261,7 +261,7 @@ fn addObjcMethnameSection(self: *InternalObject, methname: []const u8, macho_fil
sect.offset = @intCast(self.objc_methnames.items.len); sect.offset = @intCast(self.objc_methnames.items.len);
try self.objc_methnames.ensureUnusedCapacity(gpa, methname.len + 1); try self.objc_methnames.ensureUnusedCapacity(gpa, methname.len + 1);
self.objc_methnames.writer(gpa).print("{s}\x00", .{methname}) catch unreachable; self.objc_methnames.print(gpa, "{s}\x00", .{methname}) catch unreachable;
const name_str = try self.addString(gpa, "ltmp"); const name_str = try self.addString(gpa, "ltmp");
const sym_index = try self.addSymbol(gpa); const sym_index = try self.addSymbol(gpa);
@ -836,60 +836,46 @@ fn needsObjcMsgsendSymbol(self: InternalObject) bool {
return false; return false;
} }
const FormatContext = struct { const Format = struct {
self: *InternalObject, self: *InternalObject,
macho_file: *MachO, macho_file: *MachO,
};
pub fn fmtAtoms(self: *InternalObject, macho_file: *MachO) std.fmt.Formatter(formatAtoms) { fn atoms(f: Format, w: *Writer) Writer.Error!void {
return .{ .data = .{ try w.writeAll(" atoms\n");
.self = self, for (f.self.getAtoms()) |atom_index| {
.macho_file = macho_file, const atom = f.self.getAtom(atom_index) orelse continue;
} }; try w.print(" {f}\n", .{atom.fmt(f.macho_file)});
} }
fn formatAtoms(
ctx: FormatContext,
comptime unused_fmt_string: []const u8,
options: std.fmt.FormatOptions,
writer: anytype,
) !void {
_ = unused_fmt_string;
_ = options;
try writer.writeAll(" atoms\n");
for (ctx.self.getAtoms()) |atom_index| {
const atom = ctx.self.getAtom(atom_index) orelse continue;
try writer.print(" {}\n", .{atom.fmt(ctx.macho_file)});
} }
}
pub fn fmtSymtab(self: *InternalObject, macho_file: *MachO) std.fmt.Formatter(formatSymtab) { fn symtab(f: Format, w: *Writer) Writer.Error!void {
return .{ .data = .{ const macho_file = f.macho_file;
.self = self, const self = f.self;
.macho_file = macho_file, try w.writeAll(" symbols\n");
} };
}
fn formatSymtab(
ctx: FormatContext,
comptime unused_fmt_string: []const u8,
options: std.fmt.FormatOptions,
writer: anytype,
) !void {
_ = unused_fmt_string;
_ = options;
const macho_file = ctx.macho_file;
const self = ctx.self;
try writer.writeAll(" symbols\n");
for (self.symbols.items, 0..) |sym, i| { for (self.symbols.items, 0..) |sym, i| {
const ref = self.getSymbolRef(@intCast(i), macho_file); const ref = self.getSymbolRef(@intCast(i), macho_file);
if (ref.getFile(macho_file) == null) { if (ref.getFile(macho_file) == null) {
// TODO any better way of handling this? // TODO any better way of handling this?
try writer.print(" {s} : unclaimed\n", .{sym.getName(macho_file)}); try w.print(" {s} : unclaimed\n", .{sym.getName(macho_file)});
} else { } else {
try writer.print(" {}\n", .{ref.getSymbol(macho_file).?.fmt(macho_file)}); try w.print(" {f}\n", .{ref.getSymbol(macho_file).?.fmt(macho_file)});
} }
} }
}
};
pub fn fmtAtoms(self: *InternalObject, macho_file: *MachO) std.fmt.Formatter(Format, Format.atoms) {
return .{ .data = .{
.self = self,
.macho_file = macho_file,
} };
}
pub fn fmtSymtab(self: *InternalObject, macho_file: *MachO) std.fmt.Formatter(Format, Format.symtab) {
return .{ .data = .{
.self = self,
.macho_file = macho_file,
} };
} }
const Section = struct { const Section = struct {
@ -908,6 +894,7 @@ const macho = std.macho;
const mem = std.mem; const mem = std.mem;
const std = @import("std"); const std = @import("std");
const trace = @import("../../tracy.zig").trace; const trace = @import("../../tracy.zig").trace;
const Writer = std.io.Writer;
const Allocator = std.mem.Allocator; const Allocator = std.mem.Allocator;
const Atom = @import("Atom.zig"); const Atom = @import("Atom.zig");

View File

@ -72,7 +72,7 @@ pub fn parse(self: *Object, macho_file: *MachO) !void {
const tracy = trace(@src()); const tracy = trace(@src());
defer tracy.end(); defer tracy.end();
log.debug("parsing {}", .{self.fmtPath()}); log.debug("parsing {f}", .{self.fmtPath()});
const gpa = macho_file.base.comp.gpa; const gpa = macho_file.base.comp.gpa;
const handle = macho_file.getFileHandle(self.file_handle); const handle = macho_file.getFileHandle(self.file_handle);
@ -239,7 +239,7 @@ pub fn parse(self: *Object, macho_file: *MachO) !void {
if (self.platform) |platform| { if (self.platform) |platform| {
if (!macho_file.platform.eqlTarget(platform)) { if (!macho_file.platform.eqlTarget(platform)) {
try macho_file.reportParseError2(self.index, "invalid platform: {}", .{ try macho_file.reportParseError2(self.index, "invalid platform: {f}", .{
platform.fmtTarget(cpu_arch), platform.fmtTarget(cpu_arch),
}); });
return error.InvalidTarget; return error.InvalidTarget;
@ -247,7 +247,7 @@ pub fn parse(self: *Object, macho_file: *MachO) !void {
// TODO: this causes the CI to fail so I'm commenting this check out so that // TODO: this causes the CI to fail so I'm commenting this check out so that
// I can work out the rest of the changes first // I can work out the rest of the changes first
// if (macho_file.platform.version.order(platform.version) == .lt) { // if (macho_file.platform.version.order(platform.version) == .lt) {
// try macho_file.reportParseError2(self.index, "object file built for newer platform: {}: {} < {}", .{ // try macho_file.reportParseError2(self.index, "object file built for newer platform: {f}: {f} < {f}", .{
// macho_file.platform.fmtTarget(macho_file.getTarget().cpu.arch), // macho_file.platform.fmtTarget(macho_file.getTarget().cpu.arch),
// macho_file.platform.version, // macho_file.platform.version,
// platform.version, // platform.version,
@ -308,7 +308,9 @@ fn initSubsections(self: *Object, allocator: Allocator, nlists: anytype) !void {
} else nlists.len; } else nlists.len;
if (nlist_start == nlist_end or nlists[nlist_start].nlist.n_value > sect.addr) { if (nlist_start == nlist_end or nlists[nlist_start].nlist.n_value > sect.addr) {
const name = try std.fmt.allocPrintSentinel(allocator, "{s}${s}$begin", .{ sect.segName(), sect.sectName() }, 0); const name = try std.fmt.allocPrintSentinel(allocator, "{s}${s}$begin", .{
sect.segName(), sect.sectName(),
}, 0);
defer allocator.free(name); defer allocator.free(name);
const size = if (nlist_start == nlist_end) sect.size else nlists[nlist_start].nlist.n_value - sect.addr; const size = if (nlist_start == nlist_end) sect.size else nlists[nlist_start].nlist.n_value - sect.addr;
const atom_index = try self.addAtom(allocator, .{ const atom_index = try self.addAtom(allocator, .{
@ -364,7 +366,9 @@ fn initSubsections(self: *Object, allocator: Allocator, nlists: anytype) !void {
// which cannot be contained in any non-zero atom (since then this atom // which cannot be contained in any non-zero atom (since then this atom
// would exceed section boundaries). In order to facilitate this behaviour, // would exceed section boundaries). In order to facilitate this behaviour,
// we create a dummy zero-sized atom at section end (addr + size). // we create a dummy zero-sized atom at section end (addr + size).
const name = try std.fmt.allocPrintSentinel(allocator, "{s}${s}$end", .{ sect.segName(), sect.sectName() }, 0); const name = try std.fmt.allocPrintSentinel(allocator, "{s}${s}$end", .{
sect.segName(), sect.sectName(),
}, 0);
defer allocator.free(name); defer allocator.free(name);
const atom_index = try self.addAtom(allocator, .{ const atom_index = try self.addAtom(allocator, .{
.name = try self.addString(allocator, name), .name = try self.addString(allocator, name),
@ -1065,7 +1069,7 @@ fn initEhFrameRecords(self: *Object, allocator: Allocator, sect_id: u8, file: Fi
} }
} }
var it = eh_frame.Iterator{ .data = self.eh_frame_data.items }; var it: eh_frame.Iterator = .{ .br = .fixed(self.eh_frame_data.items) };
while (try it.next()) |rec| { while (try it.next()) |rec| {
switch (rec.tag) { switch (rec.tag) {
.cie => try self.cies.append(allocator, .{ .cie => try self.cies.append(allocator, .{
@ -1694,11 +1698,11 @@ pub fn updateArSize(self: *Object, macho_file: *MachO) !void {
}; };
} }
pub fn writeAr(self: Object, ar_format: Archive.Format, macho_file: *MachO, writer: anytype) !void { pub fn writeAr(self: Object, bw: *Writer, ar_format: Archive.Format, macho_file: *MachO) !void {
// Header // Header
const size = try macho_file.cast(usize, self.output_ar_state.size); const size = try macho_file.cast(usize, self.output_ar_state.size);
const basename = std.fs.path.basename(self.path.sub_path); const basename = std.fs.path.basename(self.path.sub_path);
try Archive.writeHeader(basename, size, ar_format, writer); try Archive.writeHeader(bw, basename, size, ar_format);
// Data // Data
const file = macho_file.getFileHandle(self.file_handle); const file = macho_file.getFileHandle(self.file_handle);
// TODO try using copyRangeAll // TODO try using copyRangeAll
@ -1707,7 +1711,7 @@ pub fn writeAr(self: Object, ar_format: Archive.Format, macho_file: *MachO, writ
defer gpa.free(data); defer gpa.free(data);
const amt = try file.preadAll(data, self.offset); const amt = try file.preadAll(data, self.offset);
if (amt != size) return error.InputOutput; if (amt != size) return error.InputOutput;
try writer.writeAll(data); try bw.writeAll(data);
} }
pub fn calcSymtabSize(self: *Object, macho_file: *MachO) void { pub fn calcSymtabSize(self: *Object, macho_file: *MachO) void {
@ -1861,7 +1865,7 @@ pub fn writeAtomsRelocatable(self: *Object, macho_file: *MachO) !void {
} }
gpa.free(sections_data); gpa.free(sections_data);
} }
@memset(sections_data, &[0]u8{}); @memset(sections_data, &.{});
const file = macho_file.getFileHandle(self.file_handle); const file = macho_file.getFileHandle(self.file_handle);
for (headers, 0..) |header, n_sect| { for (headers, 0..) |header, n_sect| {
@ -2512,165 +2516,114 @@ pub fn readSectionData(self: Object, allocator: Allocator, file: File.Handle, n_
return data; return data;
} }
pub fn format( const Format = struct {
self: *Object,
comptime unused_fmt_string: []const u8,
options: std.fmt.FormatOptions,
writer: anytype,
) !void {
_ = self;
_ = unused_fmt_string;
_ = options;
_ = writer;
@compileError("do not format objects directly");
}
const FormatContext = struct {
object: *Object, object: *Object,
macho_file: *MachO, macho_file: *MachO,
};
pub fn fmtAtoms(self: *Object, macho_file: *MachO) std.fmt.Formatter(formatAtoms) { fn atoms(f: Format, w: *Writer) Writer.Error!void {
return .{ .data = .{ const object = f.object;
.object = self, const macho_file = f.macho_file;
.macho_file = macho_file, try w.writeAll(" atoms\n");
} };
}
fn formatAtoms(
ctx: FormatContext,
comptime unused_fmt_string: []const u8,
options: std.fmt.FormatOptions,
writer: anytype,
) !void {
_ = unused_fmt_string;
_ = options;
const object = ctx.object;
const macho_file = ctx.macho_file;
try writer.writeAll(" atoms\n");
for (object.getAtoms()) |atom_index| { for (object.getAtoms()) |atom_index| {
const atom = object.getAtom(atom_index) orelse continue; const atom = object.getAtom(atom_index) orelse continue;
try writer.print(" {}\n", .{atom.fmt(macho_file)}); try w.print(" {f}\n", .{atom.fmt(macho_file)});
} }
} }
fn cies(f: Format, w: *Writer) Writer.Error!void {
pub fn fmtCies(self: *Object, macho_file: *MachO) std.fmt.Formatter(formatCies) { const object = f.object;
return .{ .data = .{ try w.writeAll(" cies\n");
.object = self,
.macho_file = macho_file,
} };
}
fn formatCies(
ctx: FormatContext,
comptime unused_fmt_string: []const u8,
options: std.fmt.FormatOptions,
writer: anytype,
) !void {
_ = unused_fmt_string;
_ = options;
const object = ctx.object;
try writer.writeAll(" cies\n");
for (object.cies.items, 0..) |cie, i| { for (object.cies.items, 0..) |cie, i| {
try writer.print(" cie({d}) : {}\n", .{ i, cie.fmt(ctx.macho_file) }); try w.print(" cie({d}) : {f}\n", .{ i, cie.fmt(f.macho_file) });
} }
} }
fn fdes(f: Format, w: *Writer) Writer.Error!void {
pub fn fmtFdes(self: *Object, macho_file: *MachO) std.fmt.Formatter(formatFdes) { const object = f.object;
return .{ .data = .{ try w.writeAll(" fdes\n");
.object = self,
.macho_file = macho_file,
} };
}
fn formatFdes(
ctx: FormatContext,
comptime unused_fmt_string: []const u8,
options: std.fmt.FormatOptions,
writer: anytype,
) !void {
_ = unused_fmt_string;
_ = options;
const object = ctx.object;
try writer.writeAll(" fdes\n");
for (object.fdes.items, 0..) |fde, i| { for (object.fdes.items, 0..) |fde, i| {
try writer.print(" fde({d}) : {}\n", .{ i, fde.fmt(ctx.macho_file) }); try w.print(" fde({d}) : {f}\n", .{ i, fde.fmt(f.macho_file) });
} }
} }
fn unwindRecords(f: Format, w: *Writer) Writer.Error!void {
pub fn fmtUnwindRecords(self: *Object, macho_file: *MachO) std.fmt.Formatter(formatUnwindRecords) { const object = f.object;
return .{ .data = .{ const macho_file = f.macho_file;
.object = self, try w.writeAll(" unwind records\n");
.macho_file = macho_file,
} };
}
fn formatUnwindRecords(
ctx: FormatContext,
comptime unused_fmt_string: []const u8,
options: std.fmt.FormatOptions,
writer: anytype,
) !void {
_ = unused_fmt_string;
_ = options;
const object = ctx.object;
const macho_file = ctx.macho_file;
try writer.writeAll(" unwind records\n");
for (object.unwind_records_indexes.items) |rec| { for (object.unwind_records_indexes.items) |rec| {
try writer.print(" rec({d}) : {}\n", .{ rec, object.getUnwindRecord(rec).fmt(macho_file) }); try w.print(" rec({d}) : {f}\n", .{ rec, object.getUnwindRecord(rec).fmt(macho_file) });
}
} }
}
pub fn fmtSymtab(self: *Object, macho_file: *MachO) std.fmt.Formatter(formatSymtab) { fn formatSymtab(f: Format, w: *Writer) Writer.Error!void {
return .{ .data = .{ const object = f.object;
.object = self, const macho_file = f.macho_file;
.macho_file = macho_file, try w.writeAll(" symbols\n");
} };
}
fn formatSymtab(
ctx: FormatContext,
comptime unused_fmt_string: []const u8,
options: std.fmt.FormatOptions,
writer: anytype,
) !void {
_ = unused_fmt_string;
_ = options;
const object = ctx.object;
const macho_file = ctx.macho_file;
try writer.writeAll(" symbols\n");
for (object.symbols.items, 0..) |sym, i| { for (object.symbols.items, 0..) |sym, i| {
const ref = object.getSymbolRef(@intCast(i), macho_file); const ref = object.getSymbolRef(@intCast(i), macho_file);
if (ref.getFile(macho_file) == null) { if (ref.getFile(macho_file) == null) {
// TODO any better way of handling this? // TODO any better way of handling this?
try writer.print(" {s} : unclaimed\n", .{sym.getName(macho_file)}); try w.print(" {s} : unclaimed\n", .{sym.getName(macho_file)});
} else { } else {
try writer.print(" {}\n", .{ref.getSymbol(macho_file).?.fmt(macho_file)}); try w.print(" {f}\n", .{ref.getSymbol(macho_file).?.fmt(macho_file)});
} }
} }
for (object.stab_files.items) |sf| { for (object.stab_files.items) |sf| {
try writer.print(" stabs({s},{s},{s})\n", .{ try w.print(" stabs({s},{s},{s})\n", .{
sf.getCompDir(object.*), sf.getCompDir(object.*),
sf.getTuName(object.*), sf.getTuName(object.*),
sf.getOsoPath(object.*), sf.getOsoPath(object.*),
}); });
for (sf.stabs.items) |stab| { for (sf.stabs.items) |stab| {
try writer.print(" {}", .{stab.fmt(object.*)}); try w.print(" {f}", .{stab.fmt(object.*)});
} }
} }
}
};
pub fn fmtAtoms(self: *Object, macho_file: *MachO) std.fmt.Formatter(Format, Format.atoms) {
return .{ .data = .{
.object = self,
.macho_file = macho_file,
} };
}
pub fn fmtCies(self: *Object, macho_file: *MachO) std.fmt.Formatter(Format, Format.cies) {
return .{ .data = .{
.object = self,
.macho_file = macho_file,
} };
}
pub fn fmtFdes(self: *Object, macho_file: *MachO) std.fmt.Formatter(Format, Format.fdes) {
return .{ .data = .{
.object = self,
.macho_file = macho_file,
} };
}
pub fn fmtUnwindRecords(self: *Object, macho_file: *MachO) std.fmt.Formatter(Format, Format.unwindRecords) {
return .{ .data = .{
.object = self,
.macho_file = macho_file,
} };
}
pub fn fmtSymtab(self: *Object, macho_file: *MachO) std.fmt.Formatter(Format, Format.symtab) {
return .{ .data = .{
.object = self,
.macho_file = macho_file,
} };
} }
pub fn fmtPath(self: Object) std.fmt.Formatter(Object, formatPath) { pub fn fmtPath(self: Object) std.fmt.Formatter(Object, formatPath) {
return .{ .data = self }; return .{ .data = self };
} }
fn formatPath(object: Object, writer: *std.io.Writer) std.io.Writer.Error!void { fn formatPath(object: Object, w: *Writer) Writer.Error!void {
if (object.in_archive) |ar| { if (object.in_archive) |ar| {
try writer.print("{f}({s})", .{ try w.print("{f}({s})", .{
@as(Path, ar.path), object.path.basename(), ar.path, object.path.basename(),
}); });
} else { } else {
try writer.print("{f}", .{@as(Path, object.path)}); try w.print("{f}", .{object.path});
} }
} }
@ -2724,44 +2677,27 @@ const StabFile = struct {
return object.symbols.items[index]; return object.symbols.items[index];
} }
pub fn format( const Format = struct {
stab: Stab, stab: Stab,
comptime unused_fmt_string: []const u8, object: Object,
options: std.fmt.FormatOptions,
writer: anytype,
) !void {
_ = stab;
_ = unused_fmt_string;
_ = options;
_ = writer;
@compileError("do not format stabs directly");
}
const StabFormatContext = struct { Stab, Object }; fn default(f: Stab.Format, w: *Writer) Writer.Error!void {
const stab = f.stab;
pub fn fmt(stab: Stab, object: Object) std.fmt.Formatter(format2) { const sym = stab.getSymbol(f.object).?;
return .{ .data = .{ stab, object } };
}
fn format2(
ctx: StabFormatContext,
comptime unused_fmt_string: []const u8,
options: std.fmt.FormatOptions,
writer: anytype,
) !void {
_ = unused_fmt_string;
_ = options;
const stab, const object = ctx;
const sym = stab.getSymbol(object).?;
if (stab.is_func) { if (stab.is_func) {
try writer.print("func({d})", .{stab.index.?}); try w.print("func({d})", .{stab.index.?});
} else if (sym.visibility == .global) { } else if (sym.visibility == .global) {
try writer.print("gsym({d})", .{stab.index.?}); try w.print("gsym({d})", .{stab.index.?});
} else { } else {
try writer.print("stsym({d})", .{stab.index.?}); try w.print("stsym({d})", .{stab.index.?});
} }
} }
}; };
pub fn fmt(stab: Stab, object: Object) std.fmt.Formatter(Stab.Format, Stab.Format.default) {
return .{ .data = .{ stab, object } };
}
};
}; };
const CompileUnit = struct { const CompileUnit = struct {
@ -3150,17 +3086,18 @@ const aarch64 = struct {
} }
}; };
const std = @import("std");
const assert = std.debug.assert; const assert = std.debug.assert;
const eh_frame = @import("eh_frame.zig");
const log = std.log.scoped(.link); const log = std.log.scoped(.link);
const macho = std.macho; const macho = std.macho;
const math = std.math; const math = std.math;
const mem = std.mem; const mem = std.mem;
const trace = @import("../../tracy.zig").trace;
const std = @import("std");
const Path = std.Build.Cache.Path; const Path = std.Build.Cache.Path;
const Allocator = std.mem.Allocator;
const Writer = std.io.Writer;
const Allocator = mem.Allocator; const eh_frame = @import("eh_frame.zig");
const trace = @import("../../tracy.zig").trace;
const Archive = @import("Archive.zig"); const Archive = @import("Archive.zig");
const Atom = @import("Atom.zig"); const Atom = @import("Atom.zig");
const Cie = eh_frame.Cie; const Cie = eh_frame.Cie;

View File

@ -70,22 +70,16 @@ pub fn lessThan(ctx: void, lhs: Relocation, rhs: Relocation) bool {
return lhs.offset < rhs.offset; return lhs.offset < rhs.offset;
} }
const FormatCtx = struct { Relocation, std.Target.Cpu.Arch }; pub fn fmtPretty(rel: Relocation, cpu_arch: std.Target.Cpu.Arch) std.fmt.Formatter(Format, Format.pretty) {
pub fn fmtPretty(rel: Relocation, cpu_arch: std.Target.Cpu.Arch) std.fmt.Formatter(formatPretty) {
return .{ .data = .{ rel, cpu_arch } }; return .{ .data = .{ rel, cpu_arch } };
} }
fn formatPretty( const Format = struct {
ctx: FormatCtx, relocation: Relocation,
comptime unused_fmt_string: []const u8, arch: std.Target.Cpu.Arch,
options: std.fmt.FormatOptions,
writer: anytype, fn pretty(f: Format, w: *Writer) Writer.Error!void {
) !void { try w.writeAll(switch (f.relocation.type) {
_ = options;
_ = unused_fmt_string;
const rel, const cpu_arch = ctx;
const str = switch (rel.type) {
.signed => "X86_64_RELOC_SIGNED", .signed => "X86_64_RELOC_SIGNED",
.signed1 => "X86_64_RELOC_SIGNED_1", .signed1 => "X86_64_RELOC_SIGNED_1",
.signed2 => "X86_64_RELOC_SIGNED_2", .signed2 => "X86_64_RELOC_SIGNED_2",
@ -98,29 +92,29 @@ fn formatPretty(
.got_load_pageoff => "ARM64_RELOC_GOT_LOAD_PAGEOFF12", .got_load_pageoff => "ARM64_RELOC_GOT_LOAD_PAGEOFF12",
.tlvp_page => "ARM64_RELOC_TLVP_LOAD_PAGE21", .tlvp_page => "ARM64_RELOC_TLVP_LOAD_PAGE21",
.tlvp_pageoff => "ARM64_RELOC_TLVP_LOAD_PAGEOFF12", .tlvp_pageoff => "ARM64_RELOC_TLVP_LOAD_PAGEOFF12",
.branch => switch (cpu_arch) { .branch => switch (f.arch) {
.x86_64 => "X86_64_RELOC_BRANCH", .x86_64 => "X86_64_RELOC_BRANCH",
.aarch64 => "ARM64_RELOC_BRANCH26", .aarch64 => "ARM64_RELOC_BRANCH26",
else => unreachable, else => unreachable,
}, },
.got => switch (cpu_arch) { .got => switch (f.arch) {
.x86_64 => "X86_64_RELOC_GOT", .x86_64 => "X86_64_RELOC_GOT",
.aarch64 => "ARM64_RELOC_POINTER_TO_GOT", .aarch64 => "ARM64_RELOC_POINTER_TO_GOT",
else => unreachable, else => unreachable,
}, },
.subtractor => switch (cpu_arch) { .subtractor => switch (f.arch) {
.x86_64 => "X86_64_RELOC_SUBTRACTOR", .x86_64 => "X86_64_RELOC_SUBTRACTOR",
.aarch64 => "ARM64_RELOC_SUBTRACTOR", .aarch64 => "ARM64_RELOC_SUBTRACTOR",
else => unreachable, else => unreachable,
}, },
.unsigned => switch (cpu_arch) { .unsigned => switch (f.arch) {
.x86_64 => "X86_64_RELOC_UNSIGNED", .x86_64 => "X86_64_RELOC_UNSIGNED",
.aarch64 => "ARM64_RELOC_UNSIGNED", .aarch64 => "ARM64_RELOC_UNSIGNED",
else => unreachable, else => unreachable,
}, },
}; });
try writer.writeAll(str); }
} };
pub const Type = enum { pub const Type = enum {
// x86_64 // x86_64
@ -164,10 +158,11 @@ pub const Type = enum {
const Tag = enum { local, @"extern" }; const Tag = enum { local, @"extern" };
const std = @import("std");
const assert = std.debug.assert; const assert = std.debug.assert;
const macho = std.macho; const macho = std.macho;
const math = std.math; const math = std.math;
const std = @import("std"); const Writer = std.io.Writer;
const Atom = @import("Atom.zig"); const Atom = @import("Atom.zig");
const MachO = @import("../MachO.zig"); const MachO = @import("../MachO.zig");

View File

@ -286,51 +286,30 @@ pub fn setOutputSym(symbol: Symbol, macho_file: *MachO, out: *macho.nlist_64) vo
} }
} }
pub fn format( pub fn fmt(symbol: Symbol, macho_file: *MachO) std.fmt.Formatter(Format, Format.default) {
symbol: Symbol,
comptime unused_fmt_string: []const u8,
options: std.fmt.FormatOptions,
writer: anytype,
) !void {
_ = symbol;
_ = unused_fmt_string;
_ = options;
_ = writer;
@compileError("do not format symbols directly");
}
const FormatContext = struct {
symbol: Symbol,
macho_file: *MachO,
};
pub fn fmt(symbol: Symbol, macho_file: *MachO) std.fmt.Formatter(format2) {
return .{ .data = .{ return .{ .data = .{
.symbol = symbol, .symbol = symbol,
.macho_file = macho_file, .macho_file = macho_file,
} }; } };
} }
fn format2( const Format = struct {
ctx: FormatContext, symbol: Symbol,
comptime unused_fmt_string: []const u8, macho_file: *MachO,
options: std.fmt.FormatOptions,
writer: anytype, fn format2(f: Format, w: *Writer) Writer.Error!void {
) !void { const symbol = f.symbol;
_ = options; try w.print("%{d} : {s} : @{x}", .{
_ = unused_fmt_string;
const symbol = ctx.symbol;
try writer.print("%{d} : {s} : @{x}", .{
symbol.nlist_idx, symbol.nlist_idx,
symbol.getName(ctx.macho_file), symbol.getName(f.macho_file),
symbol.getAddress(.{}, ctx.macho_file), symbol.getAddress(.{}, f.macho_file),
}); });
if (symbol.getFile(ctx.macho_file)) |file| { if (symbol.getFile(f.macho_file)) |file| {
if (symbol.getOutputSectionIndex(ctx.macho_file) != 0) { if (symbol.getOutputSectionIndex(f.macho_file) != 0) {
try writer.print(" : sect({d})", .{symbol.getOutputSectionIndex(ctx.macho_file)}); try w.print(" : sect({d})", .{symbol.getOutputSectionIndex(f.macho_file)});
} }
if (symbol.getAtom(ctx.macho_file)) |atom| { if (symbol.getAtom(f.macho_file)) |atom| {
try writer.print(" : atom({d})", .{atom.atom_index}); try w.print(" : atom({d})", .{atom.atom_index});
} }
var buf: [3]u8 = .{'_'} ** 3; var buf: [3]u8 = .{'_'} ** 3;
if (symbol.flags.@"export") buf[0] = 'E'; if (symbol.flags.@"export") buf[0] = 'E';
@ -340,17 +319,18 @@ fn format2(
.hidden => buf[2] = 'H', .hidden => buf[2] = 'H',
.global => buf[2] = 'G', .global => buf[2] = 'G',
} }
try writer.print(" : {s}", .{&buf}); try w.print(" : {s}", .{&buf});
if (symbol.flags.weak) try writer.writeAll(" : weak"); if (symbol.flags.weak) try w.writeAll(" : weak");
if (symbol.isSymbolStab(ctx.macho_file)) try writer.writeAll(" : stab"); if (symbol.isSymbolStab(f.macho_file)) try w.writeAll(" : stab");
switch (file) { switch (file) {
.zig_object => |x| try writer.print(" : zig_object({d})", .{x.index}), .zig_object => |x| try w.print(" : zig_object({d})", .{x.index}),
.internal => |x| try writer.print(" : internal({d})", .{x.index}), .internal => |x| try w.print(" : internal({d})", .{x.index}),
.object => |x| try writer.print(" : object({d})", .{x.index}), .object => |x| try w.print(" : object({d})", .{x.index}),
.dylib => |x| try writer.print(" : dylib({d})", .{x.index}), .dylib => |x| try w.print(" : dylib({d})", .{x.index}),
} }
} else try writer.writeAll(" : unresolved"); } else try w.writeAll(" : unresolved");
} }
};
pub const Flags = packed struct { pub const Flags = packed struct {
/// Whether the symbol is imported at runtime. /// Whether the symbol is imported at runtime.
@ -437,6 +417,7 @@ pub const Index = u32;
const assert = std.debug.assert; const assert = std.debug.assert;
const macho = std.macho; const macho = std.macho;
const std = @import("std"); const std = @import("std");
const Writer = std.io.Writer;
const Atom = @import("Atom.zig"); const Atom = @import("Atom.zig");
const File = @import("file.zig").File; const File = @import("file.zig").File;

View File

@ -20,16 +20,16 @@ pub fn getTargetAddress(thunk: Thunk, ref: MachO.Ref, macho_file: *MachO) u64 {
return thunk.getAddress(macho_file) + thunk.symbols.getIndex(ref).? * trampoline_size; return thunk.getAddress(macho_file) + thunk.symbols.getIndex(ref).? * trampoline_size;
} }
pub fn write(thunk: Thunk, macho_file: *MachO, writer: anytype) !void { pub fn write(thunk: Thunk, macho_file: *MachO, bw: *Writer) !void {
for (thunk.symbols.keys(), 0..) |ref, i| { for (thunk.symbols.keys(), 0..) |ref, i| {
const sym = ref.getSymbol(macho_file).?; const sym = ref.getSymbol(macho_file).?;
const saddr = thunk.getAddress(macho_file) + i * trampoline_size; const saddr = thunk.getAddress(macho_file) + i * trampoline_size;
const taddr = sym.getAddress(.{}, macho_file); const taddr = sym.getAddress(.{}, macho_file);
const pages = try aarch64.calcNumberOfPages(@intCast(saddr), @intCast(taddr)); const pages = try aarch64.calcNumberOfPages(@intCast(saddr), @intCast(taddr));
try writer.writeInt(u32, aarch64.Instruction.adrp(.x16, pages).toU32(), .little); try bw.writeInt(u32, aarch64.Instruction.adrp(.x16, pages).toU32(), .little);
const off: u12 = @truncate(taddr); const off: u12 = @truncate(taddr);
try writer.writeInt(u32, aarch64.Instruction.add(.x16, .x16, off, false).toU32(), .little); try bw.writeInt(u32, aarch64.Instruction.add(.x16, .x16, off, false).toU32(), .little);
try writer.writeInt(u32, aarch64.Instruction.br(.x16).toU32(), .little); try bw.writeInt(u32, aarch64.Instruction.br(.x16).toU32(), .little);
} }
} }
@ -61,47 +61,27 @@ pub fn writeSymtab(thunk: Thunk, macho_file: *MachO, ctx: anytype) void {
} }
} }
pub fn format( pub fn fmt(thunk: Thunk, macho_file: *MachO) std.fmt.Formatter(Format, Format.default) {
thunk: Thunk,
comptime unused_fmt_string: []const u8,
options: std.fmt.FormatOptions,
writer: anytype,
) !void {
_ = thunk;
_ = unused_fmt_string;
_ = options;
_ = writer;
@compileError("do not format Thunk directly");
}
pub fn fmt(thunk: Thunk, macho_file: *MachO) std.fmt.Formatter(format2) {
return .{ .data = .{ return .{ .data = .{
.thunk = thunk, .thunk = thunk,
.macho_file = macho_file, .macho_file = macho_file,
} }; } };
} }
const FormatContext = struct { const Format = struct {
thunk: Thunk, thunk: Thunk,
macho_file: *MachO, macho_file: *MachO,
};
fn format2( fn default(f: Format, w: *Writer) Writer.Error!void {
ctx: FormatContext, const thunk = f.thunk;
comptime unused_fmt_string: []const u8, const macho_file = f.macho_file;
options: std.fmt.FormatOptions, try w.print("@{x} : size({x})\n", .{ thunk.value, thunk.size() });
writer: anytype,
) !void {
_ = options;
_ = unused_fmt_string;
const thunk = ctx.thunk;
const macho_file = ctx.macho_file;
try writer.print("@{x} : size({x})\n", .{ thunk.value, thunk.size() });
for (thunk.symbols.keys()) |ref| { for (thunk.symbols.keys()) |ref| {
const sym = ref.getSymbol(macho_file).?; const sym = ref.getSymbol(macho_file).?;
try writer.print(" {} : {s} : @{x}\n", .{ ref, sym.getName(macho_file), sym.value }); try w.print(" {f} : {s} : @{x}\n", .{ ref, sym.getName(macho_file), sym.value });
} }
} }
};
const trampoline_size = 3 * @sizeOf(u32); const trampoline_size = 3 * @sizeOf(u32);
@ -115,6 +95,7 @@ const math = std.math;
const mem = std.mem; const mem = std.mem;
const std = @import("std"); const std = @import("std");
const trace = @import("../../tracy.zig").trace; const trace = @import("../../tracy.zig").trace;
const Writer = std.io.Writer;
const Allocator = mem.Allocator; const Allocator = mem.Allocator;
const Atom = @import("Atom.zig"); const Atom = @import("Atom.zig");

View File

@ -133,7 +133,7 @@ pub fn generate(info: *UnwindInfo, macho_file: *MachO) !void {
for (info.records.items) |ref| { for (info.records.items) |ref| {
const rec = ref.getUnwindRecord(macho_file); const rec = ref.getUnwindRecord(macho_file);
const atom = rec.getAtom(macho_file); const atom = rec.getAtom(macho_file);
log.debug("@{x}-{x} : {s} : rec({d}) : object({d}) : {}", .{ log.debug("@{x}-{x} : {s} : rec({d}) : object({d}) : {f}", .{
rec.getAtomAddress(macho_file), rec.getAtomAddress(macho_file),
rec.getAtomAddress(macho_file) + rec.length, rec.getAtomAddress(macho_file) + rec.length,
atom.getName(macho_file), atom.getName(macho_file),
@ -202,7 +202,7 @@ pub fn generate(info: *UnwindInfo, macho_file: *MachO) !void {
if (i >= max_common_encodings) break; if (i >= max_common_encodings) break;
if (slice[i].count < 2) continue; if (slice[i].count < 2) continue;
info.appendCommonEncoding(slice[i].enc); info.appendCommonEncoding(slice[i].enc);
log.debug("adding common encoding: {d} => {}", .{ i, slice[i].enc }); log.debug("adding common encoding: {d} => {f}", .{ i, slice[i].enc });
} }
} }
@ -255,7 +255,7 @@ pub fn generate(info: *UnwindInfo, macho_file: *MachO) !void {
page.kind = .compressed; page.kind = .compressed;
} }
log.debug("{}", .{page.fmt(info.*)}); log.debug("{f}", .{page.fmt(info.*)});
try info.pages.append(gpa, page); try info.pages.append(gpa, page);
} }
@ -289,13 +289,10 @@ pub fn calcSize(info: UnwindInfo) usize {
return total_size; return total_size;
} }
pub fn write(info: UnwindInfo, macho_file: *MachO, buffer: []u8) !void { pub fn write(info: UnwindInfo, macho_file: *MachO, bw: *Writer) Writer.Error!void {
const seg = macho_file.getTextSegment(); const seg = macho_file.getTextSegment();
const header = macho_file.sections.items(.header)[macho_file.unwind_info_sect_index.?]; const header = macho_file.sections.items(.header)[macho_file.unwind_info_sect_index.?];
var stream = std.io.fixedBufferStream(buffer);
const writer = stream.writer();
const common_encodings_offset: u32 = @sizeOf(macho.unwind_info_section_header); const common_encodings_offset: u32 = @sizeOf(macho.unwind_info_section_header);
const common_encodings_count: u32 = info.common_encodings_count; const common_encodings_count: u32 = info.common_encodings_count;
const personalities_offset: u32 = common_encodings_offset + common_encodings_count * @sizeOf(u32); const personalities_offset: u32 = common_encodings_offset + common_encodings_count * @sizeOf(u32);
@ -303,7 +300,7 @@ pub fn write(info: UnwindInfo, macho_file: *MachO, buffer: []u8) !void {
const indexes_offset: u32 = personalities_offset + personalities_count * @sizeOf(u32); const indexes_offset: u32 = personalities_offset + personalities_count * @sizeOf(u32);
const indexes_count: u32 = @as(u32, @intCast(info.pages.items.len + 1)); const indexes_count: u32 = @as(u32, @intCast(info.pages.items.len + 1));
try writer.writeStruct(macho.unwind_info_section_header{ try bw.writeStruct(macho.unwind_info_section_header{
.commonEncodingsArraySectionOffset = common_encodings_offset, .commonEncodingsArraySectionOffset = common_encodings_offset,
.commonEncodingsArrayCount = common_encodings_count, .commonEncodingsArrayCount = common_encodings_count,
.personalityArraySectionOffset = personalities_offset, .personalityArraySectionOffset = personalities_offset,
@ -312,11 +309,11 @@ pub fn write(info: UnwindInfo, macho_file: *MachO, buffer: []u8) !void {
.indexCount = indexes_count, .indexCount = indexes_count,
}); });
try writer.writeAll(mem.sliceAsBytes(info.common_encodings[0..info.common_encodings_count])); try bw.writeAll(mem.sliceAsBytes(info.common_encodings[0..info.common_encodings_count]));
for (info.personalities[0..info.personalities_count]) |ref| { for (info.personalities[0..info.personalities_count]) |ref| {
const sym = ref.getSymbol(macho_file).?; const sym = ref.getSymbol(macho_file).?;
try writer.writeInt(u32, @intCast(sym.getGotAddress(macho_file) - seg.vmaddr), .little); try bw.writeInt(u32, @intCast(sym.getGotAddress(macho_file) - seg.vmaddr), .little);
} }
const pages_base_offset = @as(u32, @intCast(header.size - (info.pages.items.len * second_level_page_bytes))); const pages_base_offset = @as(u32, @intCast(header.size - (info.pages.items.len * second_level_page_bytes)));
@ -325,7 +322,7 @@ pub fn write(info: UnwindInfo, macho_file: *MachO, buffer: []u8) !void {
for (info.pages.items, 0..) |page, i| { for (info.pages.items, 0..) |page, i| {
assert(page.count > 0); assert(page.count > 0);
const rec = info.records.items[page.start].getUnwindRecord(macho_file); const rec = info.records.items[page.start].getUnwindRecord(macho_file);
try writer.writeStruct(macho.unwind_info_section_header_index_entry{ try bw.writeStruct(macho.unwind_info_section_header_index_entry{
.functionOffset = @as(u32, @intCast(rec.getAtomAddress(macho_file) - seg.vmaddr)), .functionOffset = @as(u32, @intCast(rec.getAtomAddress(macho_file) - seg.vmaddr)),
.secondLevelPagesSectionOffset = @as(u32, @intCast(pages_base_offset + i * second_level_page_bytes)), .secondLevelPagesSectionOffset = @as(u32, @intCast(pages_base_offset + i * second_level_page_bytes)),
.lsdaIndexArraySectionOffset = lsda_base_offset + .lsdaIndexArraySectionOffset = lsda_base_offset +
@ -335,7 +332,7 @@ pub fn write(info: UnwindInfo, macho_file: *MachO, buffer: []u8) !void {
const last_rec = info.records.items[info.records.items.len - 1].getUnwindRecord(macho_file); const last_rec = info.records.items[info.records.items.len - 1].getUnwindRecord(macho_file);
const sentinel_address = @as(u32, @intCast(last_rec.getAtomAddress(macho_file) + last_rec.length - seg.vmaddr)); const sentinel_address = @as(u32, @intCast(last_rec.getAtomAddress(macho_file) + last_rec.length - seg.vmaddr));
try writer.writeStruct(macho.unwind_info_section_header_index_entry{ try bw.writeStruct(macho.unwind_info_section_header_index_entry{
.functionOffset = sentinel_address, .functionOffset = sentinel_address,
.secondLevelPagesSectionOffset = 0, .secondLevelPagesSectionOffset = 0,
.lsdaIndexArraySectionOffset = lsda_base_offset + .lsdaIndexArraySectionOffset = lsda_base_offset +
@ -344,23 +341,20 @@ pub fn write(info: UnwindInfo, macho_file: *MachO, buffer: []u8) !void {
for (info.lsdas.items) |index| { for (info.lsdas.items) |index| {
const rec = info.records.items[index].getUnwindRecord(macho_file); const rec = info.records.items[index].getUnwindRecord(macho_file);
try writer.writeStruct(macho.unwind_info_section_header_lsda_index_entry{ try bw.writeStruct(macho.unwind_info_section_header_lsda_index_entry{
.functionOffset = @as(u32, @intCast(rec.getAtomAddress(macho_file) - seg.vmaddr)), .functionOffset = @as(u32, @intCast(rec.getAtomAddress(macho_file) - seg.vmaddr)),
.lsdaOffset = @as(u32, @intCast(rec.getLsdaAddress(macho_file) - seg.vmaddr)), .lsdaOffset = @as(u32, @intCast(rec.getLsdaAddress(macho_file) - seg.vmaddr)),
}); });
} }
for (info.pages.items) |page| { for (info.pages.items) |page| {
const start = stream.pos; const start = bw.count;
try page.write(info, macho_file, writer); try page.write(info, macho_file, bw);
const nwritten = stream.pos - start; const nwritten = bw.count - start;
if (nwritten < second_level_page_bytes) { try bw.splatByteAll(0, math.cast(usize, second_level_page_bytes - nwritten) orelse return error.Overflow);
const padding = math.cast(usize, second_level_page_bytes - nwritten) orelse return error.Overflow;
try writer.writeByteNTimes(0, padding);
}
} }
@memset(buffer[stream.pos..], 0); @memset(bw.unusedCapacitySlice(), 0);
} }
fn getOrPutPersonalityFunction(info: *UnwindInfo, ref: MachO.Ref) error{TooManyPersonalities}!u2 { fn getOrPutPersonalityFunction(info: *UnwindInfo, ref: MachO.Ref) error{TooManyPersonalities}!u2 {
@ -455,15 +449,9 @@ pub const Encoding = extern struct {
return enc.enc == other.enc; return enc.enc == other.enc;
} }
pub fn format( pub fn format(enc: Encoding, w: *Writer, comptime unused_fmt_string: []const u8) Writer.Error!void {
enc: Encoding, comptime assert(unused_fmt_string.len == 0);
comptime unused_fmt_string: []const u8, try w.print("0x{x:0>8}", .{enc.enc});
options: std.fmt.FormatOptions,
writer: anytype,
) !void {
_ = unused_fmt_string;
_ = options;
try writer.print("0x{x:0>8}", .{enc.enc});
} }
}; };
@ -517,48 +505,28 @@ pub const Record = struct {
return lsda.getAddress(macho_file) + rec.lsda_offset; return lsda.getAddress(macho_file) + rec.lsda_offset;
} }
pub fn format( pub fn fmt(rec: Record, macho_file: *MachO) std.fmt.Formatter(Format, Format.default) {
rec: Record,
comptime unused_fmt_string: []const u8,
options: std.fmt.FormatOptions,
writer: anytype,
) !void {
_ = rec;
_ = unused_fmt_string;
_ = options;
_ = writer;
@compileError("do not format UnwindInfo.Records directly");
}
pub fn fmt(rec: Record, macho_file: *MachO) std.fmt.Formatter(format2) {
return .{ .data = .{ return .{ .data = .{
.rec = rec, .rec = rec,
.macho_file = macho_file, .macho_file = macho_file,
} }; } };
} }
const FormatContext = struct { const Format = struct {
rec: Record, rec: Record,
macho_file: *MachO, macho_file: *MachO,
};
fn format2( fn default(f: Format, w: *Writer) Writer.Error!void {
ctx: FormatContext, const rec = f.rec;
comptime unused_fmt_string: []const u8, const macho_file = f.macho_file;
options: std.fmt.FormatOptions, try w.print("{x} : len({x})", .{
writer: anytype,
) !void {
_ = unused_fmt_string;
_ = options;
const rec = ctx.rec;
const macho_file = ctx.macho_file;
try writer.print("{x} : len({x})", .{
rec.enc.enc, rec.length, rec.enc.enc, rec.length,
}); });
if (rec.enc.isDwarf(macho_file)) try writer.print(" : fde({d})", .{rec.fde}); if (rec.enc.isDwarf(macho_file)) try w.print(" : fde({d})", .{rec.fde});
try writer.print(" : {s}", .{rec.getAtom(macho_file).getName(macho_file)}); try w.print(" : {s}", .{rec.getAtom(macho_file).getName(macho_file)});
if (!rec.alive) try writer.writeAll(" : [*]"); if (!rec.alive) try w.writeAll(" : [*]");
} }
};
pub const Index = u32; pub const Index = u32;
@ -613,45 +581,25 @@ const Page = struct {
return null; return null;
} }
fn format( const Format = struct {
page: *const Page,
comptime unused_format_string: []const u8,
options: std.fmt.FormatOptions,
writer: anytype,
) !void {
_ = page;
_ = unused_format_string;
_ = options;
_ = writer;
@compileError("do not format Page directly; use page.fmt()");
}
const FormatPageContext = struct {
page: Page, page: Page,
info: UnwindInfo, info: UnwindInfo,
fn default(f: Format, w: *Writer) Writer.Error!void {
try w.writeAll("Page:\n");
try w.print(" kind: {s}\n", .{@tagName(f.page.kind)});
try w.print(" entries: {d} - {d}\n", .{
f.page.start,
f.page.start + f.page.count,
});
try w.print(" encodings (count = {d})\n", .{f.page.page_encodings_count});
for (f.page.page_encodings[0..f.page.page_encodings_count], 0..) |enc, i| {
try w.print(" {d}: {f}\n", .{ f.info.common_encodings_count + i, enc });
}
}
}; };
fn format2( fn fmt(page: Page, info: UnwindInfo) std.fmt.Formatter(Format, Format.default) {
ctx: FormatPageContext,
comptime unused_format_string: []const u8,
options: std.fmt.FormatOptions,
writer: anytype,
) @TypeOf(writer).Error!void {
_ = options;
_ = unused_format_string;
try writer.writeAll("Page:\n");
try writer.print(" kind: {s}\n", .{@tagName(ctx.page.kind)});
try writer.print(" entries: {d} - {d}\n", .{
ctx.page.start,
ctx.page.start + ctx.page.count,
});
try writer.print(" encodings (count = {d})\n", .{ctx.page.page_encodings_count});
for (ctx.page.page_encodings[0..ctx.page.page_encodings_count], 0..) |enc, i| {
try writer.print(" {d}: {}\n", .{ ctx.info.common_encodings_count + i, enc });
}
}
fn fmt(page: Page, info: UnwindInfo) std.fmt.Formatter(format2) {
return .{ .data = .{ return .{ .data = .{
.page = page, .page = page,
.info = info, .info = info,
@ -720,6 +668,7 @@ const macho = std.macho;
const math = std.math; const math = std.math;
const mem = std.mem; const mem = std.mem;
const trace = @import("../../tracy.zig").trace; const trace = @import("../../tracy.zig").trace;
const Writer = std.io.Writer;
const Allocator = mem.Allocator; const Allocator = mem.Allocator;
const Atom = @import("Atom.zig"); const Atom = @import("Atom.zig");

View File

@ -317,12 +317,12 @@ pub fn updateArSize(self: *ZigObject) void {
self.output_ar_state.size = self.data.items.len; self.output_ar_state.size = self.data.items.len;
} }
pub fn writeAr(self: ZigObject, ar_format: Archive.Format, writer: anytype) !void { pub fn writeAr(self: ZigObject, bw: *Writer, ar_format: Archive.Format) Writer.Error!void {
// Header // Header
const size = std.math.cast(usize, self.output_ar_state.size) orelse return error.Overflow; const size = std.math.cast(usize, self.output_ar_state.size) orelse return error.Overflow;
try Archive.writeHeader(self.basename, size, ar_format, writer); try Archive.writeHeader(bw, self.basename, size, ar_format);
// Data // Data
try writer.writeAll(self.data.items); try bw.writeAll(self.data.items);
} }
pub fn claimUnresolved(self: *ZigObject, macho_file: *MachO) void { pub fn claimUnresolved(self: *ZigObject, macho_file: *MachO) void {
@ -618,7 +618,7 @@ pub fn getNavVAddr(
const zcu = pt.zcu; const zcu = pt.zcu;
const ip = &zcu.intern_pool; const ip = &zcu.intern_pool;
const nav = ip.getNav(nav_index); const nav = ip.getNav(nav_index);
log.debug("getNavVAddr {}({d})", .{ nav.fqn.fmt(ip), nav_index }); log.debug("getNavVAddr {f}({d})", .{ nav.fqn.fmt(ip), nav_index });
const sym_index = if (nav.getExtern(ip)) |@"extern"| try self.getGlobalSymbol( const sym_index = if (nav.getExtern(ip)) |@"extern"| try self.getGlobalSymbol(
macho_file, macho_file,
nav.name.toSlice(ip), nav.name.toSlice(ip),
@ -884,7 +884,6 @@ pub fn updateNav(
defer debug_wip_nav.deinit(); defer debug_wip_nav.deinit();
dwarf.finishWipNav(pt, nav_index, &debug_wip_nav) catch |err| switch (err) { dwarf.finishWipNav(pt, nav_index, &debug_wip_nav) catch |err| switch (err) {
error.OutOfMemory => return error.OutOfMemory, error.OutOfMemory => return error.OutOfMemory,
error.Overflow => return error.Overflow,
else => |e| return macho_file.base.cgFail(nav_index, "failed to finish dwarf nav: {s}", .{@errorName(e)}), else => |e| return macho_file.base.cgFail(nav_index, "failed to finish dwarf nav: {s}", .{@errorName(e)}),
}; };
} }
@ -921,7 +920,6 @@ pub fn updateNav(
if (debug_wip_nav) |*wip_nav| self.dwarf.?.finishWipNav(pt, nav_index, wip_nav) catch |err| switch (err) { if (debug_wip_nav) |*wip_nav| self.dwarf.?.finishWipNav(pt, nav_index, wip_nav) catch |err| switch (err) {
error.OutOfMemory => return error.OutOfMemory, error.OutOfMemory => return error.OutOfMemory,
error.Overflow => return error.Overflow,
else => |e| return macho_file.base.cgFail(nav_index, "failed to finish dwarf nav: {s}", .{@errorName(e)}), else => |e| return macho_file.base.cgFail(nav_index, "failed to finish dwarf nav: {s}", .{@errorName(e)}),
}; };
} else if (self.dwarf) |*dwarf| try dwarf.updateComptimeNav(pt, nav_index); } else if (self.dwarf) |*dwarf| try dwarf.updateComptimeNav(pt, nav_index);
@ -943,7 +941,7 @@ fn updateNavCode(
const ip = &zcu.intern_pool; const ip = &zcu.intern_pool;
const nav = ip.getNav(nav_index); const nav = ip.getNav(nav_index);
log.debug("updateNavCode {} 0x{x}", .{ nav.fqn.fmt(ip), nav_index }); log.debug("updateNavCode {f} 0x{x}", .{ nav.fqn.fmt(ip), nav_index });
const target = &zcu.navFileScope(nav_index).mod.?.resolved_target.result; const target = &zcu.navFileScope(nav_index).mod.?.resolved_target.result;
const required_alignment = switch (pt.navAlignment(nav_index)) { const required_alignment = switch (pt.navAlignment(nav_index)) {
@ -981,7 +979,7 @@ fn updateNavCode(
if (need_realloc) { if (need_realloc) {
atom.grow(macho_file) catch |err| atom.grow(macho_file) catch |err|
return macho_file.base.cgFail(nav_index, "failed to grow atom: {s}", .{@errorName(err)}); return macho_file.base.cgFail(nav_index, "failed to grow atom: {s}", .{@errorName(err)});
log.debug("growing {} from 0x{x} to 0x{x}", .{ nav.fqn.fmt(ip), old_vaddr, atom.value }); log.debug("growing {f} from 0x{x} to 0x{x}", .{ nav.fqn.fmt(ip), old_vaddr, atom.value });
if (old_vaddr != atom.value) { if (old_vaddr != atom.value) {
sym.value = 0; sym.value = 0;
nlist.n_value = 0; nlist.n_value = 0;
@ -1023,7 +1021,7 @@ fn updateTlv(
const ip = &pt.zcu.intern_pool; const ip = &pt.zcu.intern_pool;
const nav = ip.getNav(nav_index); const nav = ip.getNav(nav_index);
log.debug("updateTlv {} (0x{x})", .{ nav.fqn.fmt(ip), nav_index }); log.debug("updateTlv {f} (0x{x})", .{ nav.fqn.fmt(ip), nav_index });
// 1. Lower TLV initializer // 1. Lower TLV initializer
const init_sym_index = try self.createTlvInitializer( const init_sym_index = try self.createTlvInitializer(
@ -1351,7 +1349,7 @@ fn updateLazySymbol(
defer code_buffer.deinit(gpa); defer code_buffer.deinit(gpa);
const name_str = blk: { const name_str = blk: {
const name = try std.fmt.allocPrint(gpa, "__lazy_{s}_{}", .{ const name = try std.fmt.allocPrint(gpa, "__lazy_{s}_{f}", .{
@tagName(lazy_sym.kind), @tagName(lazy_sym.kind),
Type.fromInterned(lazy_sym.ty).fmt(pt), Type.fromInterned(lazy_sym.ty).fmt(pt),
}); });
@ -1430,7 +1428,7 @@ pub fn deleteExport(
} orelse return; } orelse return;
const nlist_index = metadata.@"export"(self, name.toSlice(&zcu.intern_pool)) orelse return; const nlist_index = metadata.@"export"(self, name.toSlice(&zcu.intern_pool)) orelse return;
log.debug("deleting export '{}'", .{name.fmt(&zcu.intern_pool)}); log.debug("deleting export '{f}'", .{name.fmt(&zcu.intern_pool)});
const nlist = &self.symtab.items(.nlist)[nlist_index.*]; const nlist = &self.symtab.items(.nlist)[nlist_index.*];
self.symtab.items(.size)[nlist_index.*] = 0; self.symtab.items(.size)[nlist_index.*] = 0;
@ -1678,64 +1676,50 @@ pub fn asFile(self: *ZigObject) File {
return .{ .zig_object = self }; return .{ .zig_object = self };
} }
pub fn fmtSymtab(self: *ZigObject, macho_file: *MachO) std.fmt.Formatter(formatSymtab) { pub fn fmtSymtab(self: *ZigObject, macho_file: *MachO) std.fmt.Formatter(Format, Format.symtab) {
return .{ .data = .{ return .{ .data = .{
.self = self, .self = self,
.macho_file = macho_file, .macho_file = macho_file,
} }; } };
} }
const FormatContext = struct { const Format = struct {
self: *ZigObject, self: *ZigObject,
macho_file: *MachO, macho_file: *MachO,
};
fn formatSymtab( fn symtab(f: Format, w: *Writer) Writer.Error!void {
ctx: FormatContext, try w.writeAll(" symbols\n");
comptime unused_fmt_string: []const u8, const self = f.self;
options: std.fmt.FormatOptions, const macho_file = f.macho_file;
writer: anytype,
) !void {
_ = unused_fmt_string;
_ = options;
try writer.writeAll(" symbols\n");
const self = ctx.self;
const macho_file = ctx.macho_file;
for (self.symbols.items, 0..) |sym, i| { for (self.symbols.items, 0..) |sym, i| {
const ref = self.getSymbolRef(@intCast(i), macho_file); const ref = self.getSymbolRef(@intCast(i), macho_file);
if (ref.getFile(macho_file) == null) { if (ref.getFile(macho_file) == null) {
// TODO any better way of handling this? // TODO any better way of handling this?
try writer.print(" {s} : unclaimed\n", .{sym.getName(macho_file)}); try w.print(" {s} : unclaimed\n", .{sym.getName(macho_file)});
} else { } else {
try writer.print(" {}\n", .{ref.getSymbol(macho_file).?.fmt(macho_file)}); try w.print(" {f}\n", .{ref.getSymbol(macho_file).?.fmt(macho_file)});
}
} }
} }
}
pub fn fmtAtoms(self: *ZigObject, macho_file: *MachO) std.fmt.Formatter(formatAtoms) { fn atoms(f: Format, w: *Writer) Writer.Error!void {
const self = f.self;
const macho_file = f.macho_file;
try w.writeAll(" atoms\n");
for (self.getAtoms()) |atom_index| {
const atom = self.getAtom(atom_index) orelse continue;
try w.print(" {f}\n", .{atom.fmt(macho_file)});
}
}
};
pub fn fmtAtoms(self: *ZigObject, macho_file: *MachO) std.fmt.Formatter(Format, Format.atoms) {
return .{ .data = .{ return .{ .data = .{
.self = self, .self = self,
.macho_file = macho_file, .macho_file = macho_file,
} }; } };
} }
fn formatAtoms(
ctx: FormatContext,
comptime unused_fmt_string: []const u8,
options: std.fmt.FormatOptions,
writer: anytype,
) !void {
_ = unused_fmt_string;
_ = options;
const self = ctx.self;
const macho_file = ctx.macho_file;
try writer.writeAll(" atoms\n");
for (self.getAtoms()) |atom_index| {
const atom = self.getAtom(atom_index) orelse continue;
try writer.print(" {}\n", .{atom.fmt(macho_file)});
}
}
const AvMetadata = struct { const AvMetadata = struct {
symbol_index: Symbol.Index, symbol_index: Symbol.Index,
/// A list of all exports aliases of this Av. /// A list of all exports aliases of this Av.
@ -1797,6 +1781,7 @@ const mem = std.mem;
const target_util = @import("../../target.zig"); const target_util = @import("../../target.zig");
const trace = @import("../../tracy.zig").trace; const trace = @import("../../tracy.zig").trace;
const std = @import("std"); const std = @import("std");
const Writer = std.io.Writer;
const Allocator = std.mem.Allocator; const Allocator = std.mem.Allocator;
const Archive = @import("Archive.zig"); const Archive = @import("Archive.zig");

View File

@ -117,7 +117,7 @@ fn mark(roots: []*Atom, objects: []const File.Index, macho_file: *MachO) void {
fn markLive(atom: *Atom, macho_file: *MachO) void { fn markLive(atom: *Atom, macho_file: *MachO) void {
assert(atom.visited.load(.seq_cst)); assert(atom.visited.load(.seq_cst));
atom.setAlive(true); atom.setAlive(true);
track_live_log.debug("{}marking live atom({d},{s})", .{ track_live_log.debug("{f}marking live atom({d},{s})", .{
track_live_level, track_live_level,
atom.atom_index, atom.atom_index,
atom.getName(macho_file), atom.getName(macho_file),
@ -196,15 +196,9 @@ const Level = struct {
self.value += 1; self.value += 1;
} }
pub fn format( pub fn format(self: *const @This(), bw: *Writer, comptime unused_fmt_string: []const u8) Writer.Error!void {
self: *const @This(),
comptime unused_fmt_string: []const u8,
options: std.fmt.FormatOptions,
writer: anytype,
) !void {
_ = unused_fmt_string; _ = unused_fmt_string;
_ = options; try bw.splatByteAll(' ', self.value);
try writer.writeByteNTimes(' ', self.value);
} }
}; };
@ -219,6 +213,7 @@ const mem = std.mem;
const trace = @import("../../tracy.zig").trace; const trace = @import("../../tracy.zig").trace;
const track_live_log = std.log.scoped(.dead_strip_track_live); const track_live_log = std.log.scoped(.dead_strip_track_live);
const std = @import("std"); const std = @import("std");
const Writer = std.io.Writer;
const Allocator = mem.Allocator; const Allocator = mem.Allocator;
const Atom = @import("Atom.zig"); const Atom = @import("Atom.zig");

View File

@ -3,7 +3,7 @@ buffer: std.ArrayListUnmanaged(u8) = .empty,
pub const Entry = struct { pub const Entry = struct {
offset: u64, offset: u64,
segment_id: u8, segment_id: u4,
pub fn lessThan(ctx: void, entry: Entry, other: Entry) bool { pub fn lessThan(ctx: void, entry: Entry, other: Entry) bool {
_ = ctx; _ = ctx;
@ -110,33 +110,35 @@ pub fn updateSize(rebase: *Rebase, macho_file: *MachO) !void {
fn finalize(rebase: *Rebase, gpa: Allocator) !void { fn finalize(rebase: *Rebase, gpa: Allocator) !void {
if (rebase.entries.items.len == 0) return; if (rebase.entries.items.len == 0) return;
const writer = rebase.buffer.writer(gpa); var aw: std.io.Writer.Allocating = .fromArrayList(gpa, &rebase.buffer);
const bw = &aw.writer;
defer rebase.buffer = aw.toArrayList();
log.debug("rebase opcodes", .{}); log.debug("rebase opcodes", .{});
std.mem.sort(Entry, rebase.entries.items, {}, Entry.lessThan); std.mem.sort(Entry, rebase.entries.items, {}, Entry.lessThan);
try setTypePointer(writer); try setTypePointer(bw);
var start: usize = 0; var start: usize = 0;
var seg_id: ?u8 = null; var seg_id: ?u8 = null;
for (rebase.entries.items, 0..) |entry, i| { for (rebase.entries.items, 0..) |entry, i| {
if (seg_id != null and seg_id.? == entry.segment_id) continue; if (seg_id != null and seg_id.? == entry.segment_id) continue;
try finalizeSegment(rebase.entries.items[start..i], writer); try finalizeSegment(rebase.entries.items[start..i], bw);
seg_id = entry.segment_id; seg_id = entry.segment_id;
start = i; start = i;
} }
try finalizeSegment(rebase.entries.items[start..], writer); try finalizeSegment(rebase.entries.items[start..], bw);
try done(writer); try done(bw);
} }
fn finalizeSegment(entries: []const Entry, writer: anytype) !void { fn finalizeSegment(entries: []const Entry, bw: *Writer) Writer.Error!void {
if (entries.len == 0) return; if (entries.len == 0) return;
const segment_id = entries[0].segment_id; const segment_id = entries[0].segment_id;
var offset = entries[0].offset; var offset = entries[0].offset;
try setSegmentOffset(segment_id, offset, writer); try setSegmentOffset(segment_id, offset, bw);
var count: usize = 0; var count: usize = 0;
var skip: u64 = 0; var skip: u64 = 0;
@ -155,7 +157,7 @@ fn finalizeSegment(entries: []const Entry, writer: anytype) !void {
.start => { .start => {
if (offset < current_offset) { if (offset < current_offset) {
const delta = current_offset - offset; const delta = current_offset - offset;
try addAddr(delta, writer); try addAddr(delta, bw);
offset += delta; offset += delta;
} }
state = .times; state = .times;
@ -175,7 +177,7 @@ fn finalizeSegment(entries: []const Entry, writer: anytype) !void {
offset += skip; offset += skip;
i -= 1; i -= 1;
} else { } else {
try rebaseTimes(count, writer); try rebaseTimes(count, bw);
state = .start; state = .start;
i -= 1; i -= 1;
} }
@ -184,9 +186,9 @@ fn finalizeSegment(entries: []const Entry, writer: anytype) !void {
if (current_offset < offset) { if (current_offset < offset) {
count -= 1; count -= 1;
if (count == 1) { if (count == 1) {
try rebaseAddAddr(skip, writer); try rebaseAddAddr(skip, bw);
} else { } else {
try rebaseTimesSkip(count, skip, writer); try rebaseTimesSkip(count, skip, bw);
} }
state = .start; state = .start;
offset = offset - (@sizeOf(u64) + skip); offset = offset - (@sizeOf(u64) + skip);
@ -199,7 +201,7 @@ fn finalizeSegment(entries: []const Entry, writer: anytype) !void {
count += 1; count += 1;
offset += @sizeOf(u64) + skip; offset += @sizeOf(u64) + skip;
} else { } else {
try rebaseTimesSkip(count, skip, writer); try rebaseTimesSkip(count, skip, bw);
state = .start; state = .start;
i -= 1; i -= 1;
} }
@ -210,68 +212,66 @@ fn finalizeSegment(entries: []const Entry, writer: anytype) !void {
switch (state) { switch (state) {
.start => unreachable, .start => unreachable,
.times => { .times => {
try rebaseTimes(count, writer); try rebaseTimes(count, bw);
}, },
.times_skip => { .times_skip => {
try rebaseTimesSkip(count, skip, writer); try rebaseTimesSkip(count, skip, bw);
}, },
} }
} }
fn setTypePointer(writer: anytype) !void { fn setTypePointer(bw: *Writer) Writer.Error!void {
log.debug(">>> set type: {d}", .{macho.REBASE_TYPE_POINTER}); log.debug(">>> set type: {d}", .{macho.REBASE_TYPE_POINTER});
try writer.writeByte(macho.REBASE_OPCODE_SET_TYPE_IMM | @as(u4, @truncate(macho.REBASE_TYPE_POINTER))); try bw.writeByte(macho.REBASE_OPCODE_SET_TYPE_IMM | @as(u4, @intCast(macho.REBASE_TYPE_POINTER)));
} }
fn setSegmentOffset(segment_id: u8, offset: u64, writer: anytype) !void { fn setSegmentOffset(segment_id: u4, offset: u64, bw: *Writer) Writer.Error!void {
log.debug(">>> set segment: {d} and offset: {x}", .{ segment_id, offset }); log.debug(">>> set segment: {d} and offset: {x}", .{ segment_id, offset });
try writer.writeByte(macho.REBASE_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB | @as(u4, @truncate(segment_id))); try bw.writeByte(macho.REBASE_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB | @as(u4, @truncate(segment_id)));
try std.leb.writeUleb128(writer, offset); try bw.writeLeb128(offset);
} }
fn rebaseAddAddr(addr: u64, writer: anytype) !void { fn rebaseAddAddr(addr: u64, bw: *Writer) Writer.Error!void {
log.debug(">>> rebase with add: {x}", .{addr}); log.debug(">>> rebase with add: {x}", .{addr});
try writer.writeByte(macho.REBASE_OPCODE_DO_REBASE_ADD_ADDR_ULEB); try bw.writeByte(macho.REBASE_OPCODE_DO_REBASE_ADD_ADDR_ULEB);
try std.leb.writeUleb128(writer, addr); try bw.writeLeb128(addr);
} }
fn rebaseTimes(count: usize, writer: anytype) !void { fn rebaseTimes(count: usize, bw: *Writer) Writer.Error!void {
log.debug(">>> rebase with count: {d}", .{count}); log.debug(">>> rebase with count: {d}", .{count});
if (count <= 0xf) { if (count <= 0xf) {
try writer.writeByte(macho.REBASE_OPCODE_DO_REBASE_IMM_TIMES | @as(u4, @truncate(count))); try bw.writeByte(macho.REBASE_OPCODE_DO_REBASE_IMM_TIMES | @as(u4, @truncate(count)));
} else { } else {
try writer.writeByte(macho.REBASE_OPCODE_DO_REBASE_ULEB_TIMES); try bw.writeByte(macho.REBASE_OPCODE_DO_REBASE_ULEB_TIMES);
try std.leb.writeUleb128(writer, count); try bw.writeLeb128(count);
} }
} }
fn rebaseTimesSkip(count: usize, skip: u64, writer: anytype) !void { fn rebaseTimesSkip(count: usize, skip: u64, bw: *Writer) Writer.Error!void {
log.debug(">>> rebase with count: {d} and skip: {x}", .{ count, skip }); log.debug(">>> rebase with count: {d} and skip: {x}", .{ count, skip });
try writer.writeByte(macho.REBASE_OPCODE_DO_REBASE_ULEB_TIMES_SKIPPING_ULEB); try bw.writeByte(macho.REBASE_OPCODE_DO_REBASE_ULEB_TIMES_SKIPPING_ULEB);
try std.leb.writeUleb128(writer, count); try bw.writeLeb128(count);
try std.leb.writeUleb128(writer, skip); try bw.writeLeb128(skip);
} }
fn addAddr(addr: u64, writer: anytype) !void { fn addAddr(addr: u64, bw: *Writer) Writer.Error!void {
log.debug(">>> add: {x}", .{addr}); log.debug(">>> add: {x}", .{addr});
if (std.mem.isAlignedGeneric(u64, addr, @sizeOf(u64))) { if (std.math.divExact(u64, addr, @sizeOf(u64))) |scaled| {
const imm = @divExact(addr, @sizeOf(u64)); if (std.math.cast(u4, scaled)) |imm_scaled| return bw.writeByte(
if (imm <= 0xf) { macho.REBASE_OPCODE_ADD_ADDR_IMM_SCALED | imm_scaled,
try writer.writeByte(macho.REBASE_OPCODE_ADD_ADDR_IMM_SCALED | @as(u4, @truncate(imm))); );
return; } else |_| {}
} try bw.writeByte(macho.REBASE_OPCODE_ADD_ADDR_ULEB);
} try bw.writeLeb128(addr);
try writer.writeByte(macho.REBASE_OPCODE_ADD_ADDR_ULEB);
try std.leb.writeUleb128(writer, addr);
} }
fn done(writer: anytype) !void { fn done(bw: *Writer) Writer.Error!void {
log.debug(">>> done", .{}); log.debug(">>> done", .{});
try writer.writeByte(macho.REBASE_OPCODE_DONE); try bw.writeByte(macho.REBASE_OPCODE_DONE);
} }
pub fn write(rebase: Rebase, writer: anytype) !void { pub fn write(rebase: Rebase, bw: *Writer) Writer.Error!void {
try writer.writeAll(rebase.buffer.items); try bw.writeAll(rebase.buffer.items);
} }
test "rebase - no entries" { test "rebase - no entries" {
@ -654,9 +654,10 @@ const log = std.log.scoped(.link_dyld_info);
const macho = std.macho; const macho = std.macho;
const mem = std.mem; const mem = std.mem;
const testing = std.testing; const testing = std.testing;
const trace = @import("../../../tracy.zig").trace;
const Allocator = mem.Allocator; const Allocator = mem.Allocator;
const Writer = std.io.Writer;
const trace = @import("../../../tracy.zig").trace;
const File = @import("../file.zig").File; const File = @import("../file.zig").File;
const MachO = @import("../../MachO.zig"); const MachO = @import("../../MachO.zig");
const Rebase = @This(); const Rebase = @This();

View File

@ -31,7 +31,7 @@
/// The root node of the trie. /// The root node of the trie.
root: ?Node.Index = null, root: ?Node.Index = null,
buffer: std.ArrayListUnmanaged(u8) = .empty, buffer: []u8 = &.{},
nodes: std.MultiArrayList(Node) = .{}, nodes: std.MultiArrayList(Node) = .{},
edges: std.ArrayListUnmanaged(Edge) = .empty, edges: std.ArrayListUnmanaged(Edge) = .empty,
@ -123,7 +123,7 @@ pub fn updateSize(self: *Trie, macho_file: *MachO) !void {
try self.finalize(gpa); try self.finalize(gpa);
macho_file.dyld_info_cmd.export_size = mem.alignForward(u32, @intCast(self.buffer.items.len), @alignOf(u64)); macho_file.dyld_info_cmd.export_size = mem.alignForward(u32, @intCast(self.buffer.len), @alignOf(u64));
} }
/// Finalizes this trie for writing to a byte stream. /// Finalizes this trie for writing to a byte stream.
@ -138,7 +138,7 @@ fn finalize(self: *Trie, allocator: Allocator) !void {
defer ordered_nodes.deinit(); defer ordered_nodes.deinit();
try ordered_nodes.ensureTotalCapacityPrecise(self.nodes.items(.is_terminal).len); try ordered_nodes.ensureTotalCapacityPrecise(self.nodes.items(.is_terminal).len);
var fifo = std.fifo.LinearFifo(Node.Index, .Dynamic).init(allocator); var fifo = DeprecatedLinearFifo(Node.Index).init(allocator);
defer fifo.deinit(); defer fifo.deinit();
try fifo.writeItem(self.root.?); try fifo.writeItem(self.root.?);
@ -164,9 +164,11 @@ fn finalize(self: *Trie, allocator: Allocator) !void {
} }
} }
try self.buffer.ensureTotalCapacityPrecise(allocator, size); assert(self.buffer.len == 0);
self.buffer = try allocator.alloc(u8, size);
var bw: Writer = .fixed(self.buffer);
for (ordered_nodes.items) |node_index| { for (ordered_nodes.items) |node_index| {
try self.writeNode(node_index, self.buffer.writer(allocator)); try self.writeNode(node_index, &bw);
} }
} }
@ -181,17 +183,17 @@ const FinalizeNodeResult = struct {
/// Updates offset of this node in the output byte stream. /// Updates offset of this node in the output byte stream.
fn finalizeNode(self: *Trie, node_index: Node.Index, offset_in_trie: u32) !FinalizeNodeResult { fn finalizeNode(self: *Trie, node_index: Node.Index, offset_in_trie: u32) !FinalizeNodeResult {
var stream = std.io.countingWriter(std.io.null_writer); var buf: [1024]u8 = undefined;
const writer = stream.writer(); var bw: Writer = .discarding(&buf);
const slice = self.nodes.slice(); const slice = self.nodes.slice();
var node_size: u32 = 0; var node_size: u32 = 0;
if (slice.items(.is_terminal)[node_index]) { if (slice.items(.is_terminal)[node_index]) {
const export_flags = slice.items(.export_flags)[node_index]; const export_flags = slice.items(.export_flags)[node_index];
const vmaddr_offset = slice.items(.vmaddr_offset)[node_index]; const vmaddr_offset = slice.items(.vmaddr_offset)[node_index];
try leb.writeULEB128(writer, export_flags); try bw.writeLeb128(export_flags);
try leb.writeULEB128(writer, vmaddr_offset); try bw.writeLeb128(vmaddr_offset);
try leb.writeULEB128(writer, stream.bytes_written); try bw.writeLeb128(bw.count);
} else { } else {
node_size += 1; // 0x0 for non-terminal nodes node_size += 1; // 0x0 for non-terminal nodes
} }
@ -201,13 +203,13 @@ fn finalizeNode(self: *Trie, node_index: Node.Index, offset_in_trie: u32) !Final
const edge = &self.edges.items[edge_index]; const edge = &self.edges.items[edge_index];
const next_node_offset = slice.items(.trie_offset)[edge.node]; const next_node_offset = slice.items(.trie_offset)[edge.node];
node_size += @intCast(edge.label.len + 1); node_size += @intCast(edge.label.len + 1);
try leb.writeULEB128(writer, next_node_offset); try bw.writeLeb128(next_node_offset);
} }
const trie_offset = slice.items(.trie_offset)[node_index]; const trie_offset = slice.items(.trie_offset)[node_index];
const updated = offset_in_trie != trie_offset; const updated = offset_in_trie != trie_offset;
slice.items(.trie_offset)[node_index] = offset_in_trie; slice.items(.trie_offset)[node_index] = offset_in_trie;
node_size += @intCast(stream.bytes_written); node_size += @intCast(bw.count);
return .{ .node_size = node_size, .updated = updated }; return .{ .node_size = node_size, .updated = updated };
} }
@ -223,12 +225,11 @@ pub fn deinit(self: *Trie, allocator: Allocator) void {
} }
self.nodes.deinit(allocator); self.nodes.deinit(allocator);
self.edges.deinit(allocator); self.edges.deinit(allocator);
self.buffer.deinit(allocator); allocator.free(self.buffer);
} }
pub fn write(self: Trie, writer: anytype) !void { pub fn write(self: Trie, bw: *Writer) Writer.Error!void {
if (self.buffer.items.len == 0) return; try bw.writeAll(self.buffer);
try writer.writeAll(self.buffer.items);
} }
/// Writes this node to a byte stream. /// Writes this node to a byte stream.
@ -237,7 +238,7 @@ pub fn write(self: Trie, writer: anytype) !void {
/// iterate over `Trie.ordered_nodes` and call this method on each node. /// iterate over `Trie.ordered_nodes` and call this method on each node.
/// This is one of the requirements of the MachO. /// This is one of the requirements of the MachO.
/// Panics if `finalize` was not called before calling this method. /// Panics if `finalize` was not called before calling this method.
fn writeNode(self: *Trie, node_index: Node.Index, writer: anytype) !void { fn writeNode(self: *Trie, node_index: Node.Index, bw: *Writer) !void {
const slice = self.nodes.slice(); const slice = self.nodes.slice();
const edges = slice.items(.edges)[node_index]; const edges = slice.items(.edges)[node_index];
const is_terminal = slice.items(.is_terminal)[node_index]; const is_terminal = slice.items(.is_terminal)[node_index];
@ -245,36 +246,28 @@ fn writeNode(self: *Trie, node_index: Node.Index, writer: anytype) !void {
const vmaddr_offset = slice.items(.vmaddr_offset)[node_index]; const vmaddr_offset = slice.items(.vmaddr_offset)[node_index];
if (is_terminal) { if (is_terminal) {
// Terminal node info: encode export flags and vmaddr offset of this symbol. const start = bw.count;
var info_buf: [@sizeOf(u64) * 2]u8 = undefined;
var info_stream = std.io.fixedBufferStream(&info_buf);
// TODO Implement for special flags. // TODO Implement for special flags.
assert(export_flags & macho.EXPORT_SYMBOL_FLAGS_REEXPORT == 0 and assert(export_flags & macho.EXPORT_SYMBOL_FLAGS_REEXPORT == 0 and
export_flags & macho.EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER == 0); export_flags & macho.EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER == 0);
try leb.writeULEB128(info_stream.writer(), export_flags); // Terminal node info: encode export flags and vmaddr offset of this symbol.
try leb.writeULEB128(info_stream.writer(), vmaddr_offset); try bw.writeLeb128(export_flags);
try bw.writeLeb128(vmaddr_offset);
// Encode the size of the terminal node info. // Encode the size of the terminal node info.
var size_buf: [@sizeOf(u64)]u8 = undefined; try bw.writeLeb128(bw.count - start);
var size_stream = std.io.fixedBufferStream(&size_buf);
try leb.writeULEB128(size_stream.writer(), info_stream.pos);
// Now, write them to the output stream.
try writer.writeAll(size_buf[0..size_stream.pos]);
try writer.writeAll(info_buf[0..info_stream.pos]);
} else { } else {
// Non-terminal node is delimited by 0 byte. // Non-terminal node is delimited by 0 byte.
try writer.writeByte(0); try bw.writeByte(0);
} }
// Write number of edges (max legal number of edges is 256). // Write number of edges (max legal number of edges is 255).
try writer.writeByte(@as(u8, @intCast(edges.items.len))); try bw.writeByte(@intCast(edges.items.len));
for (edges.items) |edge_index| { for (edges.items) |edge_index| {
const edge = self.edges.items[edge_index]; const edge = self.edges.items[edge_index];
// Write edge label and offset to next node in trie. // Write edge label and offset to next node in trie.
try writer.writeAll(edge.label); try bw.writeAll(edge.label);
try writer.writeByte(0); try bw.writeByte(0);
try leb.writeULEB128(writer, slice.items(.trie_offset)[edge.node]); try bw.writeLeb128(slice.items(.trie_offset)[edge.node]);
} }
} }
@ -414,8 +407,10 @@ const macho = std.macho;
const mem = std.mem; const mem = std.mem;
const std = @import("std"); const std = @import("std");
const testing = std.testing; const testing = std.testing;
const trace = @import("../../../tracy.zig").trace; const Writer = std.io.Writer;
const trace = @import("../../../tracy.zig").trace;
const DeprecatedLinearFifo = @import("../../../deprecated.zig").LinearFifo;
const Allocator = mem.Allocator; const Allocator = mem.Allocator;
const MachO = @import("../../MachO.zig"); const MachO = @import("../../MachO.zig");
const Trie = @This(); const Trie = @This();

View File

@ -1,7 +1,7 @@
pub const Entry = struct { pub const Entry = struct {
target: MachO.Ref, target: MachO.Ref,
offset: u64, offset: u64,
segment_id: u8, segment_id: u4,
addend: i64, addend: i64,
pub fn lessThan(ctx: *MachO, entry: Entry, other: Entry) bool { pub fn lessThan(ctx: *MachO, entry: Entry, other: Entry) bool {
@ -20,14 +20,12 @@ pub const Bind = struct {
entries: std.ArrayListUnmanaged(Entry) = .empty, entries: std.ArrayListUnmanaged(Entry) = .empty,
buffer: std.ArrayListUnmanaged(u8) = .empty, buffer: std.ArrayListUnmanaged(u8) = .empty,
const Self = @This(); pub fn deinit(bind: *Bind, gpa: Allocator) void {
bind.entries.deinit(gpa);
pub fn deinit(self: *Self, gpa: Allocator) void { bind.buffer.deinit(gpa);
self.entries.deinit(gpa);
self.buffer.deinit(gpa);
} }
pub fn updateSize(self: *Self, macho_file: *MachO) !void { pub fn updateSize(bind: *Bind, macho_file: *MachO) !void {
const tracy = trace(@src()); const tracy = trace(@src());
defer tracy.end(); defer tracy.end();
@ -56,15 +54,12 @@ pub const Bind = struct {
const addend = rel.addend + rel.getRelocAddend(cpu_arch); const addend = rel.addend + rel.getRelocAddend(cpu_arch);
const sym = rel.getTargetSymbol(atom.*, macho_file); const sym = rel.getTargetSymbol(atom.*, macho_file);
if (sym.isTlvInit(macho_file)) continue; if (sym.isTlvInit(macho_file)) continue;
const entry = Entry{ if (sym.flags.import or (!(sym.flags.@"export" and sym.flags.weak) and sym.flags.interposable)) (try bind.entries.addOne(gpa)).* = .{
.target = rel.getTargetSymbolRef(atom.*, macho_file), .target = rel.getTargetSymbolRef(atom.*, macho_file),
.offset = atom_addr + rel_offset - seg.vmaddr, .offset = atom_addr + rel_offset - seg.vmaddr,
.segment_id = seg_id, .segment_id = seg_id,
.addend = addend, .addend = addend,
}; };
if (sym.flags.import or (!(sym.flags.@"export" and sym.flags.weak) and sym.flags.interposable)) {
try self.entries.append(gpa, entry);
}
} }
} }
} }
@ -75,15 +70,12 @@ pub const Bind = struct {
for (macho_file.got.symbols.items, 0..) |ref, idx| { for (macho_file.got.symbols.items, 0..) |ref, idx| {
const sym = ref.getSymbol(macho_file).?; const sym = ref.getSymbol(macho_file).?;
const addr = macho_file.got.getAddress(@intCast(idx), macho_file); const addr = macho_file.got.getAddress(@intCast(idx), macho_file);
const entry = Entry{ if (sym.flags.import or (sym.flags.@"export" and sym.flags.interposable and !sym.flags.weak)) (try bind.entries.addOne(gpa)).* = .{
.target = ref, .target = ref,
.offset = addr - seg.vmaddr, .offset = addr - seg.vmaddr,
.segment_id = seg_id, .segment_id = seg_id,
.addend = 0, .addend = 0,
}; };
if (sym.flags.import or (sym.flags.@"export" and sym.flags.interposable and !sym.flags.weak)) {
try self.entries.append(gpa, entry);
}
} }
} }
@ -94,15 +86,12 @@ pub const Bind = struct {
for (macho_file.stubs.symbols.items, 0..) |ref, idx| { for (macho_file.stubs.symbols.items, 0..) |ref, idx| {
const sym = ref.getSymbol(macho_file).?; const sym = ref.getSymbol(macho_file).?;
const addr = sect.addr + idx * @sizeOf(u64); const addr = sect.addr + idx * @sizeOf(u64);
const bind_entry = Entry{ if (sym.flags.import and sym.flags.weak) (try bind.entries.addOne(gpa)).* = .{
.target = ref, .target = ref,
.offset = addr - seg.vmaddr, .offset = addr - seg.vmaddr,
.segment_id = seg_id, .segment_id = seg_id,
.addend = 0, .addend = 0,
}; };
if (sym.flags.import and sym.flags.weak) {
try self.entries.append(gpa, bind_entry);
}
} }
} }
@ -113,49 +102,48 @@ pub const Bind = struct {
for (macho_file.tlv_ptr.symbols.items, 0..) |ref, idx| { for (macho_file.tlv_ptr.symbols.items, 0..) |ref, idx| {
const sym = ref.getSymbol(macho_file).?; const sym = ref.getSymbol(macho_file).?;
const addr = macho_file.tlv_ptr.getAddress(@intCast(idx), macho_file); const addr = macho_file.tlv_ptr.getAddress(@intCast(idx), macho_file);
const entry = Entry{ if (sym.flags.import or (sym.flags.@"export" and sym.flags.interposable and !sym.flags.weak)) (try bind.entries.addOne(gpa)).* = .{
.target = ref, .target = ref,
.offset = addr - seg.vmaddr, .offset = addr - seg.vmaddr,
.segment_id = seg_id, .segment_id = seg_id,
.addend = 0, .addend = 0,
}; };
if (sym.flags.import or (sym.flags.@"export" and sym.flags.interposable and !sym.flags.weak)) {
try self.entries.append(gpa, entry);
}
} }
} }
try self.finalize(gpa, macho_file); try bind.finalize(gpa, macho_file);
macho_file.dyld_info_cmd.bind_size = mem.alignForward(u32, @intCast(self.buffer.items.len), @alignOf(u64)); macho_file.dyld_info_cmd.bind_size = mem.alignForward(u32, @intCast(bind.buffer.items.len), @alignOf(u64));
} }
fn finalize(self: *Self, gpa: Allocator, ctx: *MachO) !void { fn finalize(bind: *Bind, gpa: Allocator, ctx: *MachO) !void {
if (self.entries.items.len == 0) return; if (bind.entries.items.len == 0) return;
const writer = self.buffer.writer(gpa); var aw: std.io.Writer.Allocating = .fromArrayList(gpa, &bind.buffer);
const bw = &aw.writer;
defer bind.buffer = aw.toArrayList();
log.debug("bind opcodes", .{}); log.debug("bind opcodes", .{});
std.mem.sort(Entry, self.entries.items, ctx, Entry.lessThan); std.mem.sort(Entry, bind.entries.items, ctx, Entry.lessThan);
var start: usize = 0; var start: usize = 0;
var seg_id: ?u8 = null; var seg_id: ?u8 = null;
for (self.entries.items, 0..) |entry, i| { for (bind.entries.items, 0..) |entry, i| {
if (seg_id != null and seg_id.? == entry.segment_id) continue; if (seg_id != null and seg_id.? == entry.segment_id) continue;
try finalizeSegment(self.entries.items[start..i], ctx, writer); try finalizeSegment(bind.entries.items[start..i], ctx, bw);
seg_id = entry.segment_id; seg_id = entry.segment_id;
start = i; start = i;
} }
try finalizeSegment(self.entries.items[start..], ctx, writer); try finalizeSegment(bind.entries.items[start..], ctx, bw);
try done(writer); try done(bw);
} }
fn finalizeSegment(entries: []const Entry, ctx: *MachO, writer: anytype) !void { fn finalizeSegment(entries: []const Entry, ctx: *MachO, bw: *Writer) Writer.Error!void {
if (entries.len == 0) return; if (entries.len == 0) return;
const seg_id = entries[0].segment_id; const seg_id = entries[0].segment_id;
try setSegmentOffset(seg_id, 0, writer); try setSegmentOffset(seg_id, 0, bw);
var offset: u64 = 0; var offset: u64 = 0;
var addend: i64 = 0; var addend: i64 = 0;
@ -175,15 +163,15 @@ pub const Bind = struct {
if (target == null or !target.?.eql(current.target)) { if (target == null or !target.?.eql(current.target)) {
switch (state) { switch (state) {
.start => {}, .start => {},
.bind_single => try doBind(writer), .bind_single => try doBind(bw),
.bind_times_skip => try doBindTimesSkip(count, skip, writer), .bind_times_skip => try doBindTimesSkip(count, skip, bw),
} }
state = .start; state = .start;
target = current.target; target = current.target;
const sym = current.target.getSymbol(ctx).?; const sym = current.target.getSymbol(ctx).?;
const name = sym.getName(ctx); const name = sym.getName(ctx);
const flags: u8 = if (sym.weakRef(ctx)) macho.BIND_SYMBOL_FLAGS_WEAK_IMPORT else 0; const flags: u4 = if (sym.weakRef(ctx)) macho.BIND_SYMBOL_FLAGS_WEAK_IMPORT else 0;
const ordinal: i16 = ord: { const ordinal: i16 = ord: {
if (sym.flags.interposable) break :ord macho.BIND_SPECIAL_DYLIB_FLAT_LOOKUP; if (sym.flags.interposable) break :ord macho.BIND_SPECIAL_DYLIB_FLAT_LOOKUP;
if (sym.flags.import) { if (sym.flags.import) {
@ -195,13 +183,13 @@ pub const Bind = struct {
break :ord macho.BIND_SPECIAL_DYLIB_SELF; break :ord macho.BIND_SPECIAL_DYLIB_SELF;
}; };
try setSymbol(name, flags, writer); try setSymbol(name, flags, bw);
try setTypePointer(writer); try setTypePointer(bw);
try setDylibOrdinal(ordinal, writer); try setDylibOrdinal(ordinal, bw);
if (current.addend != addend) { if (current.addend != addend) {
addend = current.addend; addend = current.addend;
try setAddend(addend, writer); try setAddend(addend, bw);
} }
} }
@ -210,11 +198,11 @@ pub const Bind = struct {
switch (state) { switch (state) {
.start => { .start => {
if (current.offset < offset) { if (current.offset < offset) {
try addAddr(@bitCast(@as(i64, @intCast(current.offset)) - @as(i64, @intCast(offset))), writer); try addAddr(@bitCast(@as(i64, @intCast(current.offset)) - @as(i64, @intCast(offset))), bw);
offset = offset - (offset - current.offset); offset = offset - (offset - current.offset);
} else if (current.offset > offset) { } else if (current.offset > offset) {
const delta = current.offset - offset; const delta = current.offset - offset;
try addAddr(delta, writer); try addAddr(delta, bw);
offset += delta; offset += delta;
} }
state = .bind_single; state = .bind_single;
@ -223,7 +211,7 @@ pub const Bind = struct {
}, },
.bind_single => { .bind_single => {
if (current.offset == offset) { if (current.offset == offset) {
try doBind(writer); try doBind(bw);
state = .start; state = .start;
} else if (current.offset > offset) { } else if (current.offset > offset) {
const delta = current.offset - offset; const delta = current.offset - offset;
@ -237,9 +225,9 @@ pub const Bind = struct {
if (current.offset < offset) { if (current.offset < offset) {
count -= 1; count -= 1;
if (count == 1) { if (count == 1) {
try doBindAddAddr(skip, writer); try doBindAddAddr(skip, bw);
} else { } else {
try doBindTimesSkip(count, skip, writer); try doBindTimesSkip(count, skip, bw);
} }
state = .start; state = .start;
offset = offset - (@sizeOf(u64) + skip); offset = offset - (@sizeOf(u64) + skip);
@ -248,7 +236,7 @@ pub const Bind = struct {
count += 1; count += 1;
offset += @sizeOf(u64) + skip; offset += @sizeOf(u64) + skip;
} else { } else {
try doBindTimesSkip(count, skip, writer); try doBindTimesSkip(count, skip, bw);
state = .start; state = .start;
i -= 1; i -= 1;
} }
@ -258,13 +246,13 @@ pub const Bind = struct {
switch (state) { switch (state) {
.start => unreachable, .start => unreachable,
.bind_single => try doBind(writer), .bind_single => try doBind(bw),
.bind_times_skip => try doBindTimesSkip(count, skip, writer), .bind_times_skip => try doBindTimesSkip(count, skip, bw),
} }
} }
pub fn write(self: Self, writer: anytype) !void { pub fn write(bind: Bind, bw: *Writer) Writer.Error!void {
try writer.writeAll(self.buffer.items); try bw.writeAll(bind.buffer.items);
} }
}; };
@ -272,14 +260,12 @@ pub const WeakBind = struct {
entries: std.ArrayListUnmanaged(Entry) = .empty, entries: std.ArrayListUnmanaged(Entry) = .empty,
buffer: std.ArrayListUnmanaged(u8) = .empty, buffer: std.ArrayListUnmanaged(u8) = .empty,
const Self = @This(); pub fn deinit(bind: *WeakBind, gpa: Allocator) void {
bind.entries.deinit(gpa);
pub fn deinit(self: *Self, gpa: Allocator) void { bind.buffer.deinit(gpa);
self.entries.deinit(gpa);
self.buffer.deinit(gpa);
} }
pub fn updateSize(self: *Self, macho_file: *MachO) !void { pub fn updateSize(bind: *WeakBind, macho_file: *MachO) !void {
const tracy = trace(@src()); const tracy = trace(@src());
defer tracy.end(); defer tracy.end();
@ -308,15 +294,12 @@ pub const WeakBind = struct {
const addend = rel.addend + rel.getRelocAddend(cpu_arch); const addend = rel.addend + rel.getRelocAddend(cpu_arch);
const sym = rel.getTargetSymbol(atom.*, macho_file); const sym = rel.getTargetSymbol(atom.*, macho_file);
if (sym.isTlvInit(macho_file)) continue; if (sym.isTlvInit(macho_file)) continue;
const entry = Entry{ if (!sym.isLocal() and sym.flags.weak) (try bind.entries.addOne(gpa)).* = .{
.target = rel.getTargetSymbolRef(atom.*, macho_file), .target = rel.getTargetSymbolRef(atom.*, macho_file),
.offset = atom_addr + rel_offset - seg.vmaddr, .offset = atom_addr + rel_offset - seg.vmaddr,
.segment_id = seg_id, .segment_id = seg_id,
.addend = addend, .addend = addend,
}; };
if (!sym.isLocal() and sym.flags.weak) {
try self.entries.append(gpa, entry);
}
} }
} }
} }
@ -327,15 +310,12 @@ pub const WeakBind = struct {
for (macho_file.got.symbols.items, 0..) |ref, idx| { for (macho_file.got.symbols.items, 0..) |ref, idx| {
const sym = ref.getSymbol(macho_file).?; const sym = ref.getSymbol(macho_file).?;
const addr = macho_file.got.getAddress(@intCast(idx), macho_file); const addr = macho_file.got.getAddress(@intCast(idx), macho_file);
const entry = Entry{ if (sym.flags.weak) (try bind.entries.addOne(gpa)).* = .{
.target = ref, .target = ref,
.offset = addr - seg.vmaddr, .offset = addr - seg.vmaddr,
.segment_id = seg_id, .segment_id = seg_id,
.addend = 0, .addend = 0,
}; };
if (sym.flags.weak) {
try self.entries.append(gpa, entry);
}
} }
} }
@ -347,15 +327,12 @@ pub const WeakBind = struct {
for (macho_file.stubs.symbols.items, 0..) |ref, idx| { for (macho_file.stubs.symbols.items, 0..) |ref, idx| {
const sym = ref.getSymbol(macho_file).?; const sym = ref.getSymbol(macho_file).?;
const addr = sect.addr + idx * @sizeOf(u64); const addr = sect.addr + idx * @sizeOf(u64);
const bind_entry = Entry{ if (sym.flags.weak) (try bind.entries.addOne(gpa)).* = .{
.target = ref, .target = ref,
.offset = addr - seg.vmaddr, .offset = addr - seg.vmaddr,
.segment_id = seg_id, .segment_id = seg_id,
.addend = 0, .addend = 0,
}; };
if (sym.flags.weak) {
try self.entries.append(gpa, bind_entry);
}
} }
} }
@ -366,49 +343,48 @@ pub const WeakBind = struct {
for (macho_file.tlv_ptr.symbols.items, 0..) |ref, idx| { for (macho_file.tlv_ptr.symbols.items, 0..) |ref, idx| {
const sym = ref.getSymbol(macho_file).?; const sym = ref.getSymbol(macho_file).?;
const addr = macho_file.tlv_ptr.getAddress(@intCast(idx), macho_file); const addr = macho_file.tlv_ptr.getAddress(@intCast(idx), macho_file);
const entry = Entry{ if (sym.flags.weak) (try bind.entries.addOne(gpa)).* = .{
.target = ref, .target = ref,
.offset = addr - seg.vmaddr, .offset = addr - seg.vmaddr,
.segment_id = seg_id, .segment_id = seg_id,
.addend = 0, .addend = 0,
}; };
if (sym.flags.weak) {
try self.entries.append(gpa, entry);
}
} }
} }
try self.finalize(gpa, macho_file); try bind.finalize(gpa, macho_file);
macho_file.dyld_info_cmd.weak_bind_size = mem.alignForward(u32, @intCast(self.buffer.items.len), @alignOf(u64)); macho_file.dyld_info_cmd.weak_bind_size = mem.alignForward(u32, @intCast(bind.buffer.items.len), @alignOf(u64));
} }
fn finalize(self: *Self, gpa: Allocator, ctx: *MachO) !void { fn finalize(bind: *WeakBind, gpa: Allocator, ctx: *MachO) !void {
if (self.entries.items.len == 0) return; if (bind.entries.items.len == 0) return;
const writer = self.buffer.writer(gpa); var aw: std.io.Writer.Allocating = .fromArrayList(gpa, &bind.buffer);
const bw = &aw.writer;
defer bind.buffer = aw.toArrayList();
log.debug("weak bind opcodes", .{}); log.debug("weak bind opcodes", .{});
std.mem.sort(Entry, self.entries.items, ctx, Entry.lessThan); std.mem.sort(Entry, bind.entries.items, ctx, Entry.lessThan);
var start: usize = 0; var start: usize = 0;
var seg_id: ?u8 = null; var seg_id: ?u8 = null;
for (self.entries.items, 0..) |entry, i| { for (bind.entries.items, 0..) |entry, i| {
if (seg_id != null and seg_id.? == entry.segment_id) continue; if (seg_id != null and seg_id.? == entry.segment_id) continue;
try finalizeSegment(self.entries.items[start..i], ctx, writer); try finalizeSegment(bind.entries.items[start..i], ctx, bw);
seg_id = entry.segment_id; seg_id = entry.segment_id;
start = i; start = i;
} }
try finalizeSegment(self.entries.items[start..], ctx, writer); try finalizeSegment(bind.entries.items[start..], ctx, bw);
try done(writer); try done(bw);
} }
fn finalizeSegment(entries: []const Entry, ctx: *MachO, writer: anytype) !void { fn finalizeSegment(entries: []const Entry, ctx: *MachO, bw: *Writer) Writer.Error!void {
if (entries.len == 0) return; if (entries.len == 0) return;
const seg_id = entries[0].segment_id; const seg_id = entries[0].segment_id;
try setSegmentOffset(seg_id, 0, writer); try setSegmentOffset(seg_id, 0, bw);
var offset: u64 = 0; var offset: u64 = 0;
var addend: i64 = 0; var addend: i64 = 0;
@ -428,8 +404,8 @@ pub const WeakBind = struct {
if (target == null or !target.?.eql(current.target)) { if (target == null or !target.?.eql(current.target)) {
switch (state) { switch (state) {
.start => {}, .start => {},
.bind_single => try doBind(writer), .bind_single => try doBind(bw),
.bind_times_skip => try doBindTimesSkip(count, skip, writer), .bind_times_skip => try doBindTimesSkip(count, skip, bw),
} }
state = .start; state = .start;
target = current.target; target = current.target;
@ -438,12 +414,12 @@ pub const WeakBind = struct {
const name = sym.getName(ctx); const name = sym.getName(ctx);
const flags: u8 = 0; // TODO NON_WEAK_DEFINITION const flags: u8 = 0; // TODO NON_WEAK_DEFINITION
try setSymbol(name, flags, writer); try setSymbol(name, flags, bw);
try setTypePointer(writer); try setTypePointer(bw);
if (current.addend != addend) { if (current.addend != addend) {
addend = current.addend; addend = current.addend;
try setAddend(addend, writer); try setAddend(addend, bw);
} }
} }
@ -452,11 +428,11 @@ pub const WeakBind = struct {
switch (state) { switch (state) {
.start => { .start => {
if (current.offset < offset) { if (current.offset < offset) {
try addAddr(@as(u64, @bitCast(@as(i64, @intCast(current.offset)) - @as(i64, @intCast(offset)))), writer); try addAddr(@as(u64, @bitCast(@as(i64, @intCast(current.offset)) - @as(i64, @intCast(offset)))), bw);
offset = offset - (offset - current.offset); offset = offset - (offset - current.offset);
} else if (current.offset > offset) { } else if (current.offset > offset) {
const delta = current.offset - offset; const delta = current.offset - offset;
try addAddr(delta, writer); try addAddr(delta, bw);
offset += delta; offset += delta;
} }
state = .bind_single; state = .bind_single;
@ -465,7 +441,7 @@ pub const WeakBind = struct {
}, },
.bind_single => { .bind_single => {
if (current.offset == offset) { if (current.offset == offset) {
try doBind(writer); try doBind(bw);
state = .start; state = .start;
} else if (current.offset > offset) { } else if (current.offset > offset) {
const delta = current.offset - offset; const delta = current.offset - offset;
@ -479,9 +455,9 @@ pub const WeakBind = struct {
if (current.offset < offset) { if (current.offset < offset) {
count -= 1; count -= 1;
if (count == 1) { if (count == 1) {
try doBindAddAddr(skip, writer); try doBindAddAddr(skip, bw);
} else { } else {
try doBindTimesSkip(count, skip, writer); try doBindTimesSkip(count, skip, bw);
} }
state = .start; state = .start;
offset = offset - (@sizeOf(u64) + skip); offset = offset - (@sizeOf(u64) + skip);
@ -490,7 +466,7 @@ pub const WeakBind = struct {
count += 1; count += 1;
offset += @sizeOf(u64) + skip; offset += @sizeOf(u64) + skip;
} else { } else {
try doBindTimesSkip(count, skip, writer); try doBindTimesSkip(count, skip, bw);
state = .start; state = .start;
i -= 1; i -= 1;
} }
@ -500,13 +476,13 @@ pub const WeakBind = struct {
switch (state) { switch (state) {
.start => unreachable, .start => unreachable,
.bind_single => try doBind(writer), .bind_single => try doBind(bw),
.bind_times_skip => try doBindTimesSkip(count, skip, writer), .bind_times_skip => try doBindTimesSkip(count, skip, bw),
} }
} }
pub fn write(self: Self, writer: anytype) !void { pub fn write(bind: WeakBind, bw: *Writer) Writer.Error!void {
try writer.writeAll(self.buffer.items); try bw.writeAll(bind.buffer.items);
} }
}; };
@ -515,15 +491,13 @@ pub const LazyBind = struct {
buffer: std.ArrayListUnmanaged(u8) = .empty, buffer: std.ArrayListUnmanaged(u8) = .empty,
offsets: std.ArrayListUnmanaged(u32) = .empty, offsets: std.ArrayListUnmanaged(u32) = .empty,
const Self = @This(); pub fn deinit(bind: *LazyBind, gpa: Allocator) void {
bind.entries.deinit(gpa);
pub fn deinit(self: *Self, gpa: Allocator) void { bind.buffer.deinit(gpa);
self.entries.deinit(gpa); bind.offsets.deinit(gpa);
self.buffer.deinit(gpa);
self.offsets.deinit(gpa);
} }
pub fn updateSize(self: *Self, macho_file: *MachO) !void { pub fn updateSize(bind: *LazyBind, macho_file: *MachO) !void {
const tracy = trace(@src()); const tracy = trace(@src());
defer tracy.end(); defer tracy.end();
@ -537,36 +511,35 @@ pub const LazyBind = struct {
for (macho_file.stubs.symbols.items, 0..) |ref, idx| { for (macho_file.stubs.symbols.items, 0..) |ref, idx| {
const sym = ref.getSymbol(macho_file).?; const sym = ref.getSymbol(macho_file).?;
const addr = sect.addr + idx * @sizeOf(u64); const addr = sect.addr + idx * @sizeOf(u64);
const bind_entry = Entry{ if ((sym.flags.import and !sym.flags.weak) or (sym.flags.interposable and !sym.flags.weak)) (try bind.entries.addOne(gpa)).* = .{
.target = ref, .target = ref,
.offset = addr - seg.vmaddr, .offset = addr - seg.vmaddr,
.segment_id = seg_id, .segment_id = seg_id,
.addend = 0, .addend = 0,
}; };
if ((sym.flags.import and !sym.flags.weak) or (sym.flags.interposable and !sym.flags.weak)) {
try self.entries.append(gpa, bind_entry);
}
} }
try self.finalize(gpa, macho_file); try bind.finalize(gpa, macho_file);
macho_file.dyld_info_cmd.lazy_bind_size = mem.alignForward(u32, @intCast(self.buffer.items.len), @alignOf(u64)); macho_file.dyld_info_cmd.lazy_bind_size = mem.alignForward(u32, @intCast(bind.buffer.items.len), @alignOf(u64));
} }
fn finalize(self: *Self, gpa: Allocator, ctx: *MachO) !void { fn finalize(bind: *LazyBind, gpa: Allocator, ctx: *MachO) !void {
try self.offsets.ensureTotalCapacityPrecise(gpa, self.entries.items.len); try bind.offsets.ensureTotalCapacityPrecise(gpa, bind.entries.items.len);
const writer = self.buffer.writer(gpa); var aw: std.io.Writer.Allocating = .fromArrayList(gpa, &bind.buffer);
const bw = &aw.writer;
defer bind.buffer = aw.toArrayList();
log.debug("lazy bind opcodes", .{}); log.debug("lazy bind opcodes", .{});
var addend: i64 = 0; var addend: i64 = 0;
for (self.entries.items) |entry| { for (bind.entries.items) |entry| {
self.offsets.appendAssumeCapacity(@intCast(self.buffer.items.len)); bind.offsets.appendAssumeCapacity(@intCast(bind.buffer.items.len));
const sym = entry.target.getSymbol(ctx).?; const sym = entry.target.getSymbol(ctx).?;
const name = sym.getName(ctx); const name = sym.getName(ctx);
const flags: u8 = if (sym.weakRef(ctx)) macho.BIND_SYMBOL_FLAGS_WEAK_IMPORT else 0; const flags: u4 = if (sym.weakRef(ctx)) macho.BIND_SYMBOL_FLAGS_WEAK_IMPORT else 0;
const ordinal: i16 = ord: { const ordinal: i16 = ord: {
if (sym.flags.interposable) break :ord macho.BIND_SPECIAL_DYLIB_FLAT_LOOKUP; if (sym.flags.interposable) break :ord macho.BIND_SPECIAL_DYLIB_FLAT_LOOKUP;
if (sym.flags.import) { if (sym.flags.import) {
@ -578,121 +551,116 @@ pub const LazyBind = struct {
break :ord macho.BIND_SPECIAL_DYLIB_SELF; break :ord macho.BIND_SPECIAL_DYLIB_SELF;
}; };
try setSegmentOffset(entry.segment_id, entry.offset, writer); try setSegmentOffset(entry.segment_id, entry.offset, bw);
try setSymbol(name, flags, writer); try setSymbol(name, flags, bw);
try setDylibOrdinal(ordinal, writer); try setDylibOrdinal(ordinal, bw);
if (entry.addend != addend) { if (entry.addend != addend) {
try setAddend(entry.addend, writer); try setAddend(entry.addend, bw);
addend = entry.addend; addend = entry.addend;
} }
try doBind(writer); try doBind(bw);
try done(writer); try done(bw);
} }
} }
pub fn write(self: Self, writer: anytype) !void { pub fn write(bind: LazyBind, bw: *Writer) Writer.Error!void {
try writer.writeAll(self.buffer.items); try bw.writeAll(bind.buffer.items);
} }
}; };
fn setSegmentOffset(segment_id: u8, offset: u64, writer: anytype) !void { fn setSegmentOffset(segment_id: u4, offset: u64, bw: *Writer) Writer.Error!void {
log.debug(">>> set segment: {d} and offset: {x}", .{ segment_id, offset }); log.debug(">>> set segment: {d} and offset: {x}", .{ segment_id, offset });
try writer.writeByte(macho.BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB | @as(u4, @truncate(segment_id))); try bw.writeByte(macho.BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB | segment_id);
try std.leb.writeUleb128(writer, offset); try bw.writeLeb128(offset);
} }
fn setSymbol(name: []const u8, flags: u8, writer: anytype) !void { fn setSymbol(name: []const u8, flags: u4, bw: *Writer) Writer.Error!void {
log.debug(">>> set symbol: {s} with flags: {x}", .{ name, flags }); log.debug(">>> set symbol: {s} with flags: {x}", .{ name, flags });
try writer.writeByte(macho.BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM | @as(u4, @truncate(flags))); try bw.writeByte(macho.BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM | flags);
try writer.writeAll(name); try bw.writeAll(name);
try writer.writeByte(0); try bw.writeByte(0);
} }
fn setTypePointer(writer: anytype) !void { fn setTypePointer(bw: *Writer) Writer.Error!void {
log.debug(">>> set type: {d}", .{macho.BIND_TYPE_POINTER}); log.debug(">>> set type: {d}", .{macho.BIND_TYPE_POINTER});
try writer.writeByte(macho.BIND_OPCODE_SET_TYPE_IMM | @as(u4, @truncate(macho.BIND_TYPE_POINTER))); try bw.writeByte(macho.BIND_OPCODE_SET_TYPE_IMM | @as(u4, @intCast(macho.BIND_TYPE_POINTER)));
} }
fn setDylibOrdinal(ordinal: i16, writer: anytype) !void { fn setDylibOrdinal(ordinal: i16, bw: *Writer) Writer.Error!void {
if (ordinal <= 0) {
switch (ordinal) { switch (ordinal) {
else => unreachable, // Invalid dylib special binding
macho.BIND_SPECIAL_DYLIB_SELF, macho.BIND_SPECIAL_DYLIB_SELF,
macho.BIND_SPECIAL_DYLIB_MAIN_EXECUTABLE, macho.BIND_SPECIAL_DYLIB_MAIN_EXECUTABLE,
macho.BIND_SPECIAL_DYLIB_FLAT_LOOKUP, macho.BIND_SPECIAL_DYLIB_FLAT_LOOKUP,
=> {}, => {
else => unreachable, // Invalid dylib special binding
}
log.debug(">>> set dylib special: {d}", .{ordinal}); log.debug(">>> set dylib special: {d}", .{ordinal});
const cast = @as(u16, @bitCast(ordinal)); try bw.writeByte(macho.BIND_OPCODE_SET_DYLIB_SPECIAL_IMM | @as(u4, @bitCast(@as(i4, @intCast(ordinal)))));
try writer.writeByte(macho.BIND_OPCODE_SET_DYLIB_SPECIAL_IMM | @as(u4, @truncate(cast))); },
} else { 1...std.math.maxInt(i16) => {
const cast = @as(u16, @bitCast(ordinal));
log.debug(">>> set dylib ordinal: {d}", .{ordinal}); log.debug(">>> set dylib ordinal: {d}", .{ordinal});
if (cast <= 0xf) { if (std.math.cast(u4, ordinal)) |imm| {
try writer.writeByte(macho.BIND_OPCODE_SET_DYLIB_ORDINAL_IMM | @as(u4, @truncate(cast))); try bw.writeByte(macho.BIND_OPCODE_SET_DYLIB_ORDINAL_IMM | imm);
} else { } else {
try writer.writeByte(macho.BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB); try bw.writeByte(macho.BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB);
try std.leb.writeUleb128(writer, cast); try bw.writeUleb128(ordinal);
} }
},
} }
} }
fn setAddend(addend: i64, writer: anytype) !void { fn setAddend(addend: i64, bw: *Writer) Writer.Error!void {
log.debug(">>> set addend: {x}", .{addend}); log.debug(">>> set addend: {x}", .{addend});
try writer.writeByte(macho.BIND_OPCODE_SET_ADDEND_SLEB); try bw.writeByte(macho.BIND_OPCODE_SET_ADDEND_SLEB);
try std.leb.writeIleb128(writer, addend); try bw.writeLeb128(addend);
} }
fn doBind(writer: anytype) !void { fn doBind(bw: *Writer) Writer.Error!void {
log.debug(">>> bind", .{}); log.debug(">>> bind", .{});
try writer.writeByte(macho.BIND_OPCODE_DO_BIND); try bw.writeByte(macho.BIND_OPCODE_DO_BIND);
} }
fn doBindAddAddr(addr: u64, writer: anytype) !void { fn doBindAddAddr(addr: u64, bw: *Writer) Writer.Error!void {
log.debug(">>> bind with add: {x}", .{addr}); log.debug(">>> bind with add: {x}", .{addr});
if (std.mem.isAlignedGeneric(u64, addr, @sizeOf(u64))) { if (std.math.divExact(u64, addr, @sizeOf(u64))) |scaled| {
const imm = @divExact(addr, @sizeOf(u64)); if (std.math.cast(u4, scaled)) |imm_scaled| return bw.writeByte(
if (imm <= 0xf) { macho.BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED | imm_scaled,
try writer.writeByte(
macho.BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED | @as(u4, @truncate(imm)),
); );
return; } else |_| {}
} try bw.writeByte(macho.BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB);
} try bw.writeLeb128(addr);
try writer.writeByte(macho.BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB);
try std.leb.writeUleb128(writer, addr);
} }
fn doBindTimesSkip(count: usize, skip: u64, writer: anytype) !void { fn doBindTimesSkip(count: usize, skip: u64, bw: *Writer) Writer.Error!void {
log.debug(">>> bind with count: {d} and skip: {x}", .{ count, skip }); log.debug(">>> bind with count: {d} and skip: {x}", .{ count, skip });
try writer.writeByte(macho.BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB); try bw.writeByte(macho.BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB);
try std.leb.writeUleb128(writer, count); try bw.writeLeb128(count);
try std.leb.writeUleb128(writer, skip); try bw.writeLeb128(skip);
} }
fn addAddr(addr: u64, writer: anytype) !void { fn addAddr(addr: u64, bw: *Writer) Writer.Error!void {
log.debug(">>> add: {x}", .{addr}); log.debug(">>> add: {x}", .{addr});
try writer.writeByte(macho.BIND_OPCODE_ADD_ADDR_ULEB); try bw.writeByte(macho.BIND_OPCODE_ADD_ADDR_ULEB);
try std.leb.writeUleb128(writer, addr); try bw.writeLeb128(addr);
} }
fn done(writer: anytype) !void { fn done(bw: *Writer) Writer.Error!void {
log.debug(">>> done", .{}); log.debug(">>> done", .{});
try writer.writeByte(macho.BIND_OPCODE_DONE); try bw.writeByte(macho.BIND_OPCODE_DONE);
} }
const std = @import("std");
const assert = std.debug.assert; const assert = std.debug.assert;
const leb = std.leb; const leb = std.leb;
const log = std.log.scoped(.link_dyld_info); const log = std.log.scoped(.link_dyld_info);
const macho = std.macho; const macho = std.macho;
const mem = std.mem; const mem = std.mem;
const testing = std.testing; const testing = std.testing;
const trace = @import("../../../tracy.zig").trace; const Allocator = std.mem.Allocator;
const std = @import("std"); const Writer = std.io.Writer;
const Allocator = mem.Allocator; const trace = @import("../../../tracy.zig").trace;
const File = @import("../file.zig").File; const File = @import("../file.zig").File;
const MachO = @import("../../MachO.zig"); const MachO = @import("../../MachO.zig");
const Symbol = @import("../Symbol.zig"); const Symbol = @import("../Symbol.zig");

View File

@ -12,36 +12,33 @@ pub const Cie = struct {
const tracy = trace(@src()); const tracy = trace(@src());
defer tracy.end(); defer tracy.end();
const data = cie.getData(macho_file); var r: std.io.Reader = .fixed(cie.getData(macho_file));
const aug = std.mem.sliceTo(@as([*:0]const u8, @ptrCast(data.ptr + 9)), 0);
try r.discard(9);
const aug = try r.takeSentinel(0);
if (aug[0] != 'z') return; // TODO should we error out? if (aug[0] != 'z') return; // TODO should we error out?
var stream = std.io.fixedBufferStream(data[9 + aug.len + 1 ..]); _ = try r.takeLeb128(u64); // code alignment factor
var creader = std.io.countingReader(stream.reader()); _ = try r.takeLeb128(u64); // data alignment factor
const reader = creader.reader(); _ = try r.takeLeb128(u64); // return address register
_ = try r.takeLeb128(u64); // augmentation data length
_ = try leb.readUleb128(u64, reader); // code alignment factor
_ = try leb.readUleb128(u64, reader); // data alignment factor
_ = try leb.readUleb128(u64, reader); // return address register
_ = try leb.readUleb128(u64, reader); // augmentation data length
for (aug[1..]) |ch| switch (ch) { for (aug[1..]) |ch| switch (ch) {
'R' => { 'R' => {
const enc = try reader.readByte(); const enc = try r.takeByte();
if (enc != DW_EH_PE.pcrel | DW_EH_PE.absptr) { if (enc != DW_EH_PE.pcrel | DW_EH_PE.absptr) {
@panic("unexpected pointer encoding"); // TODO error @panic("unexpected pointer encoding"); // TODO error
} }
}, },
'P' => { 'P' => {
const enc = try reader.readByte(); const enc = try r.takeByte();
if (enc != DW_EH_PE.pcrel | DW_EH_PE.indirect | DW_EH_PE.sdata4) { if (enc != DW_EH_PE.pcrel | DW_EH_PE.indirect | DW_EH_PE.sdata4) {
@panic("unexpected personality pointer encoding"); // TODO error @panic("unexpected personality pointer encoding"); // TODO error
} }
_ = try reader.readInt(u32, .little); // personality pointer _ = try r.takeInt(u32, .little); // personality pointer
}, },
'L' => { 'L' => {
const enc = try reader.readByte(); const enc = try r.takeByte();
switch (enc & DW_EH_PE.type_mask) { switch (enc & DW_EH_PE.type_mask) {
DW_EH_PE.sdata4 => cie.lsda_size = .p32, DW_EH_PE.sdata4 => cie.lsda_size = .p32,
DW_EH_PE.absptr => cie.lsda_size = .p64, DW_EH_PE.absptr => cie.lsda_size = .p64,
@ -81,46 +78,26 @@ pub const Cie = struct {
return true; return true;
} }
pub fn format( pub fn fmt(cie: Cie, macho_file: *MachO) std.fmt.Formatter(Format, Format.default) {
cie: Cie,
comptime unused_fmt_string: []const u8,
options: std.fmt.FormatOptions,
writer: anytype,
) !void {
_ = cie;
_ = unused_fmt_string;
_ = options;
_ = writer;
@compileError("do not format CIEs directly");
}
pub fn fmt(cie: Cie, macho_file: *MachO) std.fmt.Formatter(format2) {
return .{ .data = .{ return .{ .data = .{
.cie = cie, .cie = cie,
.macho_file = macho_file, .macho_file = macho_file,
} }; } };
} }
const FormatContext = struct { const Format = struct {
cie: Cie, cie: Cie,
macho_file: *MachO, macho_file: *MachO,
};
fn format2( fn default(f: Format, w: *Writer) Writer.Error!void {
ctx: FormatContext, const cie = f.cie;
comptime unused_fmt_string: []const u8, try w.print("@{x} : size({x})", .{
options: std.fmt.FormatOptions,
writer: anytype,
) !void {
_ = unused_fmt_string;
_ = options;
const cie = ctx.cie;
try writer.print("@{x} : size({x})", .{
cie.offset, cie.offset,
cie.getSize(), cie.getSize(),
}); });
if (!cie.alive) try writer.writeAll(" : [*]"); if (!cie.alive) try w.writeAll(" : [*]");
} }
};
pub const Index = u32; pub const Index = u32;
@ -148,12 +125,16 @@ pub const Fde = struct {
const tracy = trace(@src()); const tracy = trace(@src());
defer tracy.end(); defer tracy.end();
const data = fde.getData(macho_file);
const object = fde.getObject(macho_file); const object = fde.getObject(macho_file);
const sect = object.sections.items(.header)[object.eh_frame_sect_index.?]; const sect = object.sections.items(.header)[object.eh_frame_sect_index.?];
var br: std.io.Reader = .fixed(fde.getData(macho_file));
try br.discard(4);
const cie_ptr = try br.takeInt(u32, .little);
const pc_begin = try br.takeInt(i64, .little);
// Parse target atom index // Parse target atom index
const pc_begin = std.mem.readInt(i64, data[8..][0..8], .little);
const taddr: u64 = @intCast(@as(i64, @intCast(sect.addr + fde.offset + 8)) + pc_begin); const taddr: u64 = @intCast(@as(i64, @intCast(sect.addr + fde.offset + 8)) + pc_begin);
fde.atom = object.findAtom(taddr) orelse { fde.atom = object.findAtom(taddr) orelse {
try macho_file.reportParseError2(object.index, "{s},{s}: 0x{x}: invalid function reference in FDE", .{ try macho_file.reportParseError2(object.index, "{s},{s}: 0x{x}: invalid function reference in FDE", .{
@ -165,7 +146,6 @@ pub const Fde = struct {
fde.atom_offset = @intCast(taddr - atom.getInputAddress(macho_file)); fde.atom_offset = @intCast(taddr - atom.getInputAddress(macho_file));
// Associate with a CIE // Associate with a CIE
const cie_ptr = std.mem.readInt(u32, data[4..8], .little);
const cie_offset = fde.offset + 4 - cie_ptr; const cie_offset = fde.offset + 4 - cie_ptr;
const cie_index = for (object.cies.items, 0..) |cie, cie_index| { const cie_index = for (object.cies.items, 0..) |cie, cie_index| {
if (cie.offset == cie_offset) break @as(Cie.Index, @intCast(cie_index)); if (cie.offset == cie_offset) break @as(Cie.Index, @intCast(cie_index));
@ -183,14 +163,12 @@ pub const Fde = struct {
// Parse LSDA atom index if any // Parse LSDA atom index if any
if (cie.lsda_size) |lsda_size| { if (cie.lsda_size) |lsda_size| {
var stream = std.io.fixedBufferStream(data[24..]); try br.discard(8);
var creader = std.io.countingReader(stream.reader()); _ = try br.takeLeb128(u64); // augmentation length
const reader = creader.reader(); fde.lsda_ptr_offset = @intCast(br.seek);
_ = try leb.readUleb128(u64, reader); // augmentation length
fde.lsda_ptr_offset = @intCast(creader.bytes_read + 24);
const lsda_ptr = switch (lsda_size) { const lsda_ptr = switch (lsda_size) {
.p32 => try reader.readInt(i32, .little), .p32 => try br.takeInt(i32, .little),
.p64 => try reader.readInt(i64, .little), .p64 => try br.takeInt(i64, .little),
}; };
const lsda_addr: u64 = @intCast(@as(i64, @intCast(sect.addr + fde.offset + fde.lsda_ptr_offset)) + lsda_ptr); const lsda_addr: u64 = @intCast(@as(i64, @intCast(sect.addr + fde.offset + fde.lsda_ptr_offset)) + lsda_ptr);
fde.lsda = object.findAtom(lsda_addr) orelse { fde.lsda = object.findAtom(lsda_addr) orelse {
@ -231,56 +209,35 @@ pub const Fde = struct {
return fde.getObject(macho_file).getAtom(fde.lsda); return fde.getObject(macho_file).getAtom(fde.lsda);
} }
pub fn format( pub fn fmt(fde: Fde, macho_file: *MachO) std.fmt.Formatter(Format, Format.default) {
fde: Fde,
comptime unused_fmt_string: []const u8,
options: std.fmt.FormatOptions,
writer: anytype,
) !void {
_ = fde;
_ = unused_fmt_string;
_ = options;
_ = writer;
@compileError("do not format FDEs directly");
}
pub fn fmt(fde: Fde, macho_file: *MachO) std.fmt.Formatter(format2) {
return .{ .data = .{ return .{ .data = .{
.fde = fde, .fde = fde,
.macho_file = macho_file, .macho_file = macho_file,
} }; } };
} }
const FormatContext = struct { const Format = struct {
fde: Fde, fde: Fde,
macho_file: *MachO, macho_file: *MachO,
};
fn format2( fn default(f: Format, w: *Writer) Writer.Error!void {
ctx: FormatContext, const fde = f.fde;
comptime unused_fmt_string: []const u8, const macho_file = f.macho_file;
options: std.fmt.FormatOptions, try w.print("@{x} : size({x}) : cie({d}) : {s}", .{
writer: anytype,
) !void {
_ = unused_fmt_string;
_ = options;
const fde = ctx.fde;
const macho_file = ctx.macho_file;
try writer.print("@{x} : size({x}) : cie({d}) : {s}", .{
fde.offset, fde.offset,
fde.getSize(), fde.getSize(),
fde.cie, fde.cie,
fde.getAtom(macho_file).getName(macho_file), fde.getAtom(macho_file).getName(macho_file),
}); });
if (!fde.alive) try writer.writeAll(" : [*]"); if (!fde.alive) try w.writeAll(" : [*]");
} }
};
pub const Index = u32; pub const Index = u32;
}; };
pub const Iterator = struct { pub const Iterator = struct {
data: []const u8, reader: *std.io.Reader,
pos: u32 = 0,
pub const Record = struct { pub const Record = struct {
tag: enum { fde, cie }, tag: enum { fde, cie },
@ -289,21 +246,19 @@ pub const Iterator = struct {
}; };
pub fn next(it: *Iterator) !?Record { pub fn next(it: *Iterator) !?Record {
if (it.pos >= it.data.len) return null; const r = it.reader;
if (r.seek >= r.storageBuffer().len) return null;
var stream = std.io.fixedBufferStream(it.data[it.pos..]); const size = try r.takeInt(u32, .little);
const reader = stream.reader();
const size = try reader.readInt(u32, .little);
if (size == 0xFFFFFFFF) @panic("DWARF CFI is 32bit on macOS"); if (size == 0xFFFFFFFF) @panic("DWARF CFI is 32bit on macOS");
const id = try reader.readInt(u32, .little); const id = try r.takeInt(u32, .little);
const record = Record{ const record: Record = .{
.tag = if (id == 0) .cie else .fde, .tag = if (id == 0) .cie else .fde,
.offset = it.pos, .offset = @intCast(r.seek),
.size = size, .size = size,
}; };
it.pos += size + 4; try r.discard(size);
return record; return record;
} }
@ -545,6 +500,7 @@ const math = std.math;
const mem = std.mem; const mem = std.mem;
const std = @import("std"); const std = @import("std");
const trace = @import("../../tracy.zig").trace; const trace = @import("../../tracy.zig").trace;
const Writer = std.io.Writer;
const Allocator = std.mem.Allocator; const Allocator = std.mem.Allocator;
const Atom = @import("Atom.zig"); const Atom = @import("Atom.zig");

View File

@ -14,12 +14,12 @@ pub const File = union(enum) {
return .{ .data = file }; return .{ .data = file };
} }
fn formatPath(file: File, writer: *std.io.Writer) std.io.Writer.Error!void { fn formatPath(file: File, w: *Writer) Writer.Error!void {
switch (file) { switch (file) {
.zig_object => |zo| try writer.writeAll(zo.basename), .zig_object => |zo| try w.writeAll(zo.basename),
.internal => try writer.writeAll("internal"), .internal => try w.writeAll("internal"),
.object => |x| try writer.print("{}", .{x.fmtPath()}), .object => |x| try w.print("{f}", .{x.fmtPath()}),
.dylib => |dl| try writer.print("{}", .{@as(Path, dl.path)}), .dylib => |dl| try w.print("{f}", .{@as(Path, dl.path)}),
} }
} }
@ -321,11 +321,11 @@ pub const File = union(enum) {
}; };
} }
pub fn writeAr(file: File, ar_format: Archive.Format, macho_file: *MachO, writer: anytype) !void { pub fn writeAr(file: File, bw: *Writer, ar_format: Archive.Format, macho_file: *MachO) Writer.Error!void {
return switch (file) { return switch (file) {
.dylib, .internal => unreachable, .dylib, .internal => unreachable,
.zig_object => |x| x.writeAr(ar_format, writer), .zig_object => |x| x.writeAr(bw, ar_format),
.object => |x| x.writeAr(ar_format, macho_file, writer), .object => |x| x.writeAr(bw, ar_format, macho_file),
}; };
} }
@ -364,6 +364,7 @@ const log = std.log.scoped(.link);
const macho = std.macho; const macho = std.macho;
const Allocator = std.mem.Allocator; const Allocator = std.mem.Allocator;
const Path = std.Build.Cache.Path; const Path = std.Build.Cache.Path;
const Writer = std.io.Writer;
const trace = @import("../../tracy.zig").trace; const trace = @import("../../tracy.zig").trace;
const Archive = @import("Archive.zig"); const Archive = @import("Archive.zig");

View File

@ -3,6 +3,7 @@ const assert = std.debug.assert;
const log = std.log.scoped(.link); const log = std.log.scoped(.link);
const macho = std.macho; const macho = std.macho;
const mem = std.mem; const mem = std.mem;
const Writer = std.io.Writer;
const Allocator = mem.Allocator; const Allocator = mem.Allocator;
const DebugSymbols = @import("DebugSymbols.zig"); const DebugSymbols = @import("DebugSymbols.zig");
@ -180,23 +181,20 @@ pub fn calcMinHeaderPadSize(macho_file: *MachO) !u32 {
return offset; return offset;
} }
pub fn writeDylinkerLC(writer: anytype) !void { pub fn writeDylinkerLC(bw: *Writer) Writer.Error!void {
const name_len = mem.sliceTo(default_dyld_path, 0).len; const name_len = mem.sliceTo(default_dyld_path, 0).len;
const cmdsize = @as(u32, @intCast(mem.alignForward( const cmdsize = @as(u32, @intCast(mem.alignForward(
u64, u64,
@sizeOf(macho.dylinker_command) + name_len, @sizeOf(macho.dylinker_command) + name_len,
@sizeOf(u64), @sizeOf(u64),
))); )));
try writer.writeStruct(macho.dylinker_command{ try bw.writeStruct(macho.dylinker_command{
.cmd = .LOAD_DYLINKER, .cmd = .LOAD_DYLINKER,
.cmdsize = cmdsize, .cmdsize = cmdsize,
.name = @sizeOf(macho.dylinker_command), .name = @sizeOf(macho.dylinker_command),
}); });
try writer.writeAll(mem.sliceTo(default_dyld_path, 0)); try bw.writeAll(mem.sliceTo(default_dyld_path, 0));
const padding = cmdsize - @sizeOf(macho.dylinker_command) - name_len; try bw.splatByteAll(0, cmdsize - @sizeOf(macho.dylinker_command) - name_len);
if (padding > 0) {
try writer.writeByteNTimes(0, padding);
}
} }
const WriteDylibLCCtx = struct { const WriteDylibLCCtx = struct {
@ -207,14 +205,14 @@ const WriteDylibLCCtx = struct {
compatibility_version: u32 = 0x10000, compatibility_version: u32 = 0x10000,
}; };
pub fn writeDylibLC(ctx: WriteDylibLCCtx, writer: anytype) !void { pub fn writeDylibLC(ctx: WriteDylibLCCtx, bw: *Writer) !void {
const name_len = ctx.name.len + 1; const name_len = ctx.name.len + 1;
const cmdsize = @as(u32, @intCast(mem.alignForward( const cmdsize: u32 = @intCast(mem.alignForward(
u64, u64,
@sizeOf(macho.dylib_command) + name_len, @sizeOf(macho.dylib_command) + name_len,
@sizeOf(u64), @sizeOf(u64),
))); ));
try writer.writeStruct(macho.dylib_command{ try bw.writeStruct(macho.dylib_command{
.cmd = ctx.cmd, .cmd = ctx.cmd,
.cmdsize = cmdsize, .cmdsize = cmdsize,
.dylib = .{ .dylib = .{
@ -224,12 +222,9 @@ pub fn writeDylibLC(ctx: WriteDylibLCCtx, writer: anytype) !void {
.compatibility_version = ctx.compatibility_version, .compatibility_version = ctx.compatibility_version,
}, },
}); });
try writer.writeAll(ctx.name); try bw.writeAll(ctx.name);
try writer.writeByte(0); try bw.writeByte(0);
const padding = cmdsize - @sizeOf(macho.dylib_command) - name_len; try bw.splatByteAll(0, cmdsize - @sizeOf(macho.dylib_command) - name_len);
if (padding > 0) {
try writer.writeByteNTimes(0, padding);
}
} }
pub fn writeDylibIdLC(macho_file: *MachO, writer: anytype) !void { pub fn writeDylibIdLC(macho_file: *MachO, writer: anytype) !void {
@ -258,26 +253,23 @@ pub fn writeDylibIdLC(macho_file: *MachO, writer: anytype) !void {
}, writer); }, writer);
} }
pub fn writeRpathLC(rpath: []const u8, writer: anytype) !void { pub fn writeRpathLC(bw: *Writer, rpath: []const u8) !void {
const rpath_len = rpath.len + 1; const rpath_len = rpath.len + 1;
const cmdsize = @as(u32, @intCast(mem.alignForward( const cmdsize = @as(u32, @intCast(mem.alignForward(
u64, u64,
@sizeOf(macho.rpath_command) + rpath_len, @sizeOf(macho.rpath_command) + rpath_len,
@sizeOf(u64), @sizeOf(u64),
))); )));
try writer.writeStruct(macho.rpath_command{ try bw.writeStruct(macho.rpath_command{
.cmdsize = cmdsize, .cmdsize = cmdsize,
.path = @sizeOf(macho.rpath_command), .path = @sizeOf(macho.rpath_command),
}); });
try writer.writeAll(rpath); try bw.writeAll(rpath);
try writer.writeByte(0); try bw.writeByte(0);
const padding = cmdsize - @sizeOf(macho.rpath_command) - rpath_len; try bw.splatByteAll(0, cmdsize - @sizeOf(macho.rpath_command) - rpath_len);
if (padding > 0) {
try writer.writeByteNTimes(0, padding);
}
} }
pub fn writeVersionMinLC(platform: MachO.Platform, sdk_version: ?std.SemanticVersion, writer: anytype) !void { pub fn writeVersionMinLC(bw: *Writer, platform: MachO.Platform, sdk_version: ?std.SemanticVersion) Writer.Error!void {
const cmd: macho.LC = switch (platform.os_tag) { const cmd: macho.LC = switch (platform.os_tag) {
.macos => .VERSION_MIN_MACOSX, .macos => .VERSION_MIN_MACOSX,
.ios => .VERSION_MIN_IPHONEOS, .ios => .VERSION_MIN_IPHONEOS,
@ -285,7 +277,7 @@ pub fn writeVersionMinLC(platform: MachO.Platform, sdk_version: ?std.SemanticVer
.watchos => .VERSION_MIN_WATCHOS, .watchos => .VERSION_MIN_WATCHOS,
else => unreachable, else => unreachable,
}; };
try writer.writeAll(mem.asBytes(&macho.version_min_command{ try bw.writeAll(mem.asBytes(&macho.version_min_command{
.cmd = cmd, .cmd = cmd,
.version = platform.toAppleVersion(), .version = platform.toAppleVersion(),
.sdk = if (sdk_version) |ver| .sdk = if (sdk_version) |ver|
@ -295,9 +287,9 @@ pub fn writeVersionMinLC(platform: MachO.Platform, sdk_version: ?std.SemanticVer
})); }));
} }
pub fn writeBuildVersionLC(platform: MachO.Platform, sdk_version: ?std.SemanticVersion, writer: anytype) !void { pub fn writeBuildVersionLC(bw: *Writer, platform: MachO.Platform, sdk_version: ?std.SemanticVersion) Writer.Error!void {
const cmdsize = @sizeOf(macho.build_version_command) + @sizeOf(macho.build_tool_version); const cmdsize = @sizeOf(macho.build_version_command) + @sizeOf(macho.build_tool_version);
try writer.writeStruct(macho.build_version_command{ try bw.writeStruct(macho.build_version_command{
.cmdsize = cmdsize, .cmdsize = cmdsize,
.platform = platform.toApplePlatform(), .platform = platform.toApplePlatform(),
.minos = platform.toAppleVersion(), .minos = platform.toAppleVersion(),
@ -307,7 +299,7 @@ pub fn writeBuildVersionLC(platform: MachO.Platform, sdk_version: ?std.SemanticV
platform.toAppleVersion(), platform.toAppleVersion(),
.ntools = 1, .ntools = 1,
}); });
try writer.writeAll(mem.asBytes(&macho.build_tool_version{ try bw.writeAll(mem.asBytes(&macho.build_tool_version{
.tool = .ZIG, .tool = .ZIG,
.version = 0x0, .version = 0x0,
})); }));

View File

@ -20,13 +20,13 @@ pub fn flushObject(macho_file: *MachO, comp: *Compilation, module_obj_path: ?Pat
// the *only* input file over. // the *only* input file over.
const path = positionals.items[0].path().?; const path = positionals.items[0].path().?;
const in_file = path.root_dir.handle.openFile(path.sub_path, .{}) catch |err| const in_file = path.root_dir.handle.openFile(path.sub_path, .{}) catch |err|
return diags.fail("failed to open {}: {s}", .{ path, @errorName(err) }); return diags.fail("failed to open {f}: {s}", .{ path, @errorName(err) });
const stat = in_file.stat() catch |err| const stat = in_file.stat() catch |err|
return diags.fail("failed to stat {}: {s}", .{ path, @errorName(err) }); return diags.fail("failed to stat {f}: {s}", .{ path, @errorName(err) });
const amt = in_file.copyRangeAll(0, macho_file.base.file.?, 0, stat.size) catch |err| const amt = in_file.copyRangeAll(0, macho_file.base.file.?, 0, stat.size) catch |err|
return diags.fail("failed to copy range of file {}: {s}", .{ path, @errorName(err) }); return diags.fail("failed to copy range of file {f}: {s}", .{ path, @errorName(err) });
if (amt != stat.size) if (amt != stat.size)
return diags.fail("unexpected short write in copy range of file {}", .{path}); return diags.fail("unexpected short write in copy range of file {f}", .{path});
return; return;
} }
@ -62,7 +62,7 @@ pub fn flushObject(macho_file: *MachO, comp: *Compilation, module_obj_path: ?Pat
allocateSegment(macho_file); allocateSegment(macho_file);
if (build_options.enable_logging) { if (build_options.enable_logging) {
state_log.debug("{}", .{macho_file.dumpState()}); state_log.debug("{f}", .{macho_file.dumpState()});
} }
try writeSections(macho_file); try writeSections(macho_file);
@ -126,7 +126,7 @@ pub fn flushStaticLib(macho_file: *MachO, comp: *Compilation, module_obj_path: ?
allocateSegment(macho_file); allocateSegment(macho_file);
if (build_options.enable_logging) { if (build_options.enable_logging) {
state_log.debug("{}", .{macho_file.dumpState()}); state_log.debug("{f}", .{macho_file.dumpState()});
} }
try writeSections(macho_file); try writeSections(macho_file);
@ -202,38 +202,30 @@ pub fn flushStaticLib(macho_file: *MachO, comp: *Compilation, module_obj_path: ?
}; };
if (build_options.enable_logging) { if (build_options.enable_logging) {
state_log.debug("ar_symtab\n{}\n", .{ar_symtab.fmt(macho_file)}); state_log.debug("ar_symtab\n{f}\n", .{ar_symtab.fmt(macho_file)});
} }
var buffer = std.ArrayList(u8).init(gpa); var bw: Writer = .fixed(try gpa.alloc(u8, total_size));
defer buffer.deinit(); defer gpa.free(bw.buffer);
try buffer.ensureTotalCapacityPrecise(total_size);
const writer = buffer.writer();
// Write magic // Write magic
try writer.writeAll(Archive.ARMAG); bw.writeAll(Archive.ARMAG) catch unreachable;
// Write symtab // Write symtab
ar_symtab.write(format, macho_file, writer) catch |err| switch (err) { ar_symtab.write(&bw, format, macho_file) catch |err| {
error.OutOfMemory => return error.OutOfMemory, return diags.fail("failed to write archive symbol table: {s}", .{@errorName(err)});
else => |e| return diags.fail("failed to write archive symbol table: {s}", .{@errorName(e)}),
}; };
// Write object files // Write object files
for (files.items) |index| { for (files.items) |index| {
const aligned = mem.alignForward(usize, buffer.items.len, 2); bw.splatByteAll(0, mem.alignForward(usize, bw.end, 2) - bw.end) catch unreachable;
const padding = aligned - buffer.items.len; macho_file.getFile(index).?.writeAr(&bw, format, macho_file) catch |err|
if (padding > 0) {
try writer.writeByteNTimes(0, padding);
}
macho_file.getFile(index).?.writeAr(format, macho_file, writer) catch |err|
return diags.fail("failed to write archive: {s}", .{@errorName(err)}); return diags.fail("failed to write archive: {s}", .{@errorName(err)});
} }
assert(buffer.items.len == total_size); assert(bw.end == bw.buffer.len);
try macho_file.setEndPos(bw.end);
try macho_file.setEndPos(total_size); try macho_file.pwriteAll(bw.buffer, 0);
try macho_file.pwriteAll(buffer.items, 0);
if (diags.hasErrors()) return error.LinkFailure; if (diags.hasErrors()) return error.LinkFailure;
} }
@ -672,7 +664,7 @@ fn writeCompactUnwindWorker(macho_file: *MachO, object: *Object) void {
diags.addError("failed to write '__LD,__eh_frame' section: {s}", .{@errorName(err)}); diags.addError("failed to write '__LD,__eh_frame' section: {s}", .{@errorName(err)});
} }
fn writeSectionsToFile(macho_file: *MachO) !void { fn writeSectionsToFile(macho_file: *MachO) link.File.FlushError!void {
const tracy = trace(@src()); const tracy = trace(@src());
defer tracy.end(); defer tracy.end();
@ -689,12 +681,8 @@ fn writeSectionsToFile(macho_file: *MachO) !void {
fn writeLoadCommands(macho_file: *MachO) error{ LinkFailure, OutOfMemory }!struct { usize, usize } { fn writeLoadCommands(macho_file: *MachO) error{ LinkFailure, OutOfMemory }!struct { usize, usize } {
const gpa = macho_file.base.comp.gpa; const gpa = macho_file.base.comp.gpa;
const needed_size = load_commands.calcLoadCommandsSizeObject(macho_file); var bw: Writer = .fixed(try gpa.alloc(u8, load_commands.calcLoadCommandsSizeObject(macho_file)));
const buffer = try gpa.alloc(u8, needed_size); defer gpa.free(bw.buffer);
defer gpa.free(buffer);
var stream = std.io.fixedBufferStream(buffer);
const writer = stream.writer();
var ncmds: usize = 0; var ncmds: usize = 0;
@ -702,47 +690,31 @@ fn writeLoadCommands(macho_file: *MachO) error{ LinkFailure, OutOfMemory }!struc
{ {
assert(macho_file.segments.items.len == 1); assert(macho_file.segments.items.len == 1);
const seg = macho_file.segments.items[0]; const seg = macho_file.segments.items[0];
writer.writeStruct(seg) catch |err| switch (err) { bw.writeStruct(seg) catch unreachable;
error.NoSpaceLeft => unreachable,
};
for (macho_file.sections.items(.header)) |header| { for (macho_file.sections.items(.header)) |header| {
writer.writeStruct(header) catch |err| switch (err) { bw.writeStruct(header) catch unreachable;
error.NoSpaceLeft => unreachable,
};
} }
ncmds += 1; ncmds += 1;
} }
writer.writeStruct(macho_file.data_in_code_cmd) catch |err| switch (err) { bw.writeStruct(macho_file.data_in_code_cmd) catch unreachable;
error.NoSpaceLeft => unreachable,
};
ncmds += 1; ncmds += 1;
writer.writeStruct(macho_file.symtab_cmd) catch |err| switch (err) { bw.writeStruct(macho_file.symtab_cmd) catch unreachable;
error.NoSpaceLeft => unreachable,
};
ncmds += 1; ncmds += 1;
writer.writeStruct(macho_file.dysymtab_cmd) catch |err| switch (err) { bw.writeStruct(macho_file.dysymtab_cmd) catch unreachable;
error.NoSpaceLeft => unreachable,
};
ncmds += 1; ncmds += 1;
if (macho_file.platform.isBuildVersionCompatible()) { if (macho_file.platform.isBuildVersionCompatible()) {
load_commands.writeBuildVersionLC(macho_file.platform, macho_file.sdk_version, writer) catch |err| switch (err) { load_commands.writeBuildVersionLC(&bw, macho_file.platform, macho_file.sdk_version) catch unreachable;
error.NoSpaceLeft => unreachable,
};
ncmds += 1; ncmds += 1;
} else { } else {
load_commands.writeVersionMinLC(macho_file.platform, macho_file.sdk_version, writer) catch |err| switch (err) { load_commands.writeVersionMinLC(&bw, macho_file.platform, macho_file.sdk_version) catch unreachable;
error.NoSpaceLeft => unreachable,
};
ncmds += 1; ncmds += 1;
} }
assert(stream.pos == needed_size); assert(bw.end == bw.buffer.len);
try macho_file.pwriteAll(bw.buffer, @sizeOf(macho.mach_header_64));
try macho_file.pwriteAll(buffer, @sizeOf(macho.mach_header_64)); return .{ ncmds, bw.end };
return .{ ncmds, buffer.len };
} }
fn writeHeader(macho_file: *MachO, ncmds: usize, sizeofcmds: usize) !void { fn writeHeader(macho_file: *MachO, ncmds: usize, sizeofcmds: usize) !void {
@ -784,6 +756,7 @@ const macho = std.macho;
const math = std.math; const math = std.math;
const mem = std.mem; const mem = std.mem;
const state_log = std.log.scoped(.link_state); const state_log = std.log.scoped(.link_state);
const Writer = std.io.Writer;
const Archive = @import("Archive.zig"); const Archive = @import("Archive.zig");
const Atom = @import("Atom.zig"); const Atom = @import("Atom.zig");

View File

@ -27,44 +27,37 @@ pub const GotSection = struct {
return got.symbols.items.len * @sizeOf(u64); return got.symbols.items.len * @sizeOf(u64);
} }
pub fn write(got: GotSection, macho_file: *MachO, writer: anytype) !void { pub fn write(got: GotSection, macho_file: *MachO, bw: *Writer) !void {
const tracy = trace(@src()); const tracy = trace(@src());
defer tracy.end(); defer tracy.end();
for (got.symbols.items) |ref| { for (got.symbols.items) |ref| {
const sym = ref.getSymbol(macho_file).?; const sym = ref.getSymbol(macho_file).?;
const value = if (sym.flags.import) @as(u64, 0) else sym.getAddress(.{}, macho_file); const value = if (sym.flags.import) @as(u64, 0) else sym.getAddress(.{}, macho_file);
try writer.writeInt(u64, value, .little); try bw.writeInt(u64, value, .little);
} }
} }
const FormatCtx = struct { const Format = struct {
got: GotSection, got: GotSection,
macho_file: *MachO, macho_file: *MachO,
};
pub fn fmt(got: GotSection, macho_file: *MachO) std.fmt.Formatter(format2) { pub fn print(f: Format, w: *Writer) Writer.Error!void {
return .{ .data = .{ .got = got, .macho_file = macho_file } }; for (f.got.symbols.items, 0..) |ref, i| {
} const symbol = ref.getSymbol(f.macho_file).?;
try w.print(" {d}@0x{x} => {f}@0x{x} ({s})\n", .{
pub fn format2(
ctx: FormatCtx,
comptime unused_fmt_string: []const u8,
options: std.fmt.FormatOptions,
writer: anytype,
) !void {
_ = options;
_ = unused_fmt_string;
for (ctx.got.symbols.items, 0..) |ref, i| {
const symbol = ref.getSymbol(ctx.macho_file).?;
try writer.print(" {d}@0x{x} => {d}@0x{x} ({s})\n", .{
i, i,
symbol.getGotAddress(ctx.macho_file), symbol.getGotAddress(f.macho_file),
ref, ref,
symbol.getAddress(.{}, ctx.macho_file), symbol.getAddress(.{}, f.macho_file),
symbol.getName(ctx.macho_file), symbol.getName(f.macho_file),
}); });
} }
} }
};
pub fn fmt(got: GotSection, macho_file: *MachO) std.fmt.Formatter(Format, Format.print) {
return .{ .data = .{ .got = got, .macho_file = macho_file } };
}
}; };
pub const StubsSection = struct { pub const StubsSection = struct {
@ -96,7 +89,7 @@ pub const StubsSection = struct {
return stubs.symbols.items.len * header.reserved2; return stubs.symbols.items.len * header.reserved2;
} }
pub fn write(stubs: StubsSection, macho_file: *MachO, writer: anytype) !void { pub fn write(stubs: StubsSection, macho_file: *MachO, bw: *Writer) !void {
const tracy = trace(@src()); const tracy = trace(@src());
defer tracy.end(); defer tracy.end();
const cpu_arch = macho_file.getTarget().cpu.arch; const cpu_arch = macho_file.getTarget().cpu.arch;
@ -108,54 +101,47 @@ pub const StubsSection = struct {
const target = laptr_sect.addr + idx * @sizeOf(u64); const target = laptr_sect.addr + idx * @sizeOf(u64);
switch (cpu_arch) { switch (cpu_arch) {
.x86_64 => { .x86_64 => {
try writer.writeAll(&.{ 0xff, 0x25 }); try bw.writeAll(&.{ 0xff, 0x25 });
try writer.writeInt(i32, @intCast(target - source - 2 - 4), .little); try bw.writeInt(i32, @intCast(target - source - 2 - 4), .little);
}, },
.aarch64 => { .aarch64 => {
// TODO relax if possible // TODO relax if possible
const pages = try aarch64.calcNumberOfPages(@intCast(source), @intCast(target)); const pages = try aarch64.calcNumberOfPages(@intCast(source), @intCast(target));
try writer.writeInt(u32, aarch64.Instruction.adrp(.x16, pages).toU32(), .little); try bw.writeInt(u32, aarch64.Instruction.adrp(.x16, pages).toU32(), .little);
const off = try math.divExact(u12, @truncate(target), 8); const off = try math.divExact(u12, @truncate(target), 8);
try writer.writeInt( try bw.writeInt(
u32, u32,
aarch64.Instruction.ldr(.x16, .x16, aarch64.Instruction.LoadStoreOffset.imm(off)).toU32(), aarch64.Instruction.ldr(.x16, .x16, aarch64.Instruction.LoadStoreOffset.imm(off)).toU32(),
.little, .little,
); );
try writer.writeInt(u32, aarch64.Instruction.br(.x16).toU32(), .little); try bw.writeInt(u32, aarch64.Instruction.br(.x16).toU32(), .little);
}, },
else => unreachable, else => unreachable,
} }
} }
} }
const FormatCtx = struct { pub fn fmt(stubs: StubsSection, macho_file: *MachO) std.fmt.Formatter(Format, Format.print) {
stubs: StubsSection,
macho_file: *MachO,
};
pub fn fmt(stubs: StubsSection, macho_file: *MachO) std.fmt.Formatter(format2) {
return .{ .data = .{ .stubs = stubs, .macho_file = macho_file } }; return .{ .data = .{ .stubs = stubs, .macho_file = macho_file } };
} }
pub fn format2( const Format = struct {
ctx: FormatCtx, stubs: StubsSection,
comptime unused_fmt_string: []const u8, macho_file: *MachO,
options: std.fmt.FormatOptions,
writer: anytype, pub fn print(f: Format, w: *Writer) Writer.Error!void {
) !void { for (f.stubs.symbols.items, 0..) |ref, i| {
_ = options; const symbol = ref.getSymbol(f.macho_file).?;
_ = unused_fmt_string; try w.print(" {d}@0x{x} => {f}@0x{x} ({s})\n", .{
for (ctx.stubs.symbols.items, 0..) |ref, i| {
const symbol = ref.getSymbol(ctx.macho_file).?;
try writer.print(" {d}@0x{x} => {d}@0x{x} ({s})\n", .{
i, i,
symbol.getStubsAddress(ctx.macho_file), symbol.getStubsAddress(f.macho_file),
ref, ref,
symbol.getAddress(.{}, ctx.macho_file), symbol.getAddress(.{}, f.macho_file),
symbol.getName(ctx.macho_file), symbol.getName(f.macho_file),
}); });
} }
} }
};
}; };
pub const StubsHelperSection = struct { pub const StubsHelperSection = struct {
@ -189,11 +175,11 @@ pub const StubsHelperSection = struct {
return s; return s;
} }
pub fn write(stubs_helper: StubsHelperSection, macho_file: *MachO, writer: anytype) !void { pub fn write(stubs_helper: StubsHelperSection, macho_file: *MachO, bw: *Writer) !void {
const tracy = trace(@src()); const tracy = trace(@src());
defer tracy.end(); defer tracy.end();
try stubs_helper.writePreamble(macho_file, writer); try stubs_helper.writePreamble(macho_file, bw);
const cpu_arch = macho_file.getTarget().cpu.arch; const cpu_arch = macho_file.getTarget().cpu.arch;
const sect = macho_file.sections.items(.header)[macho_file.stubs_helper_sect_index.?]; const sect = macho_file.sections.items(.header)[macho_file.stubs_helper_sect_index.?];
@ -209,24 +195,24 @@ pub const StubsHelperSection = struct {
const target: i64 = @intCast(sect.addr); const target: i64 = @intCast(sect.addr);
switch (cpu_arch) { switch (cpu_arch) {
.x86_64 => { .x86_64 => {
try writer.writeByte(0x68); try bw.writeByte(0x68);
try writer.writeInt(u32, offset, .little); try bw.writeInt(u32, offset, .little);
try writer.writeByte(0xe9); try bw.writeByte(0xe9);
try writer.writeInt(i32, @intCast(target - source - 6 - 4), .little); try bw.writeInt(i32, @intCast(target - source - 6 - 4), .little);
}, },
.aarch64 => { .aarch64 => {
const literal = blk: { const literal = blk: {
const div_res = try std.math.divExact(u64, entry_size - @sizeOf(u32), 4); const div_res = try std.math.divExact(u64, entry_size - @sizeOf(u32), 4);
break :blk std.math.cast(u18, div_res) orelse return error.Overflow; break :blk std.math.cast(u18, div_res) orelse return error.Overflow;
}; };
try writer.writeInt(u32, aarch64.Instruction.ldrLiteral( try bw.writeInt(u32, aarch64.Instruction.ldrLiteral(
.w16, .w16,
literal, literal,
).toU32(), .little); ).toU32(), .little);
const disp = math.cast(i28, @as(i64, @intCast(target)) - @as(i64, @intCast(source + 4))) orelse const disp = math.cast(i28, @as(i64, @intCast(target)) - @as(i64, @intCast(source + 4))) orelse
return error.Overflow; return error.Overflow;
try writer.writeInt(u32, aarch64.Instruction.b(disp).toU32(), .little); try bw.writeInt(u32, aarch64.Instruction.b(disp).toU32(), .little);
try writer.writeAll(&.{ 0x0, 0x0, 0x0, 0x0 }); try bw.writeAll(&.{ 0x0, 0x0, 0x0, 0x0 });
}, },
else => unreachable, else => unreachable,
} }
@ -234,7 +220,7 @@ pub const StubsHelperSection = struct {
} }
} }
fn writePreamble(stubs_helper: StubsHelperSection, macho_file: *MachO, writer: anytype) !void { fn writePreamble(stubs_helper: StubsHelperSection, macho_file: *MachO, bw: *Writer) !void {
_ = stubs_helper; _ = stubs_helper;
const obj = macho_file.getInternalObject().?; const obj = macho_file.getInternalObject().?;
const cpu_arch = macho_file.getTarget().cpu.arch; const cpu_arch = macho_file.getTarget().cpu.arch;
@ -249,21 +235,21 @@ pub const StubsHelperSection = struct {
}; };
switch (cpu_arch) { switch (cpu_arch) {
.x86_64 => { .x86_64 => {
try writer.writeAll(&.{ 0x4c, 0x8d, 0x1d }); try bw.writeAll(&.{ 0x4c, 0x8d, 0x1d });
try writer.writeInt(i32, @intCast(dyld_private_addr - sect.addr - 3 - 4), .little); try bw.writeInt(i32, @intCast(dyld_private_addr - sect.addr - 3 - 4), .little);
try writer.writeAll(&.{ 0x41, 0x53, 0xff, 0x25 }); try bw.writeAll(&.{ 0x41, 0x53, 0xff, 0x25 });
try writer.writeInt(i32, @intCast(dyld_stub_binder_addr - sect.addr - 11 - 4), .little); try bw.writeInt(i32, @intCast(dyld_stub_binder_addr - sect.addr - 11 - 4), .little);
try writer.writeByte(0x90); try bw.writeByte(0x90);
}, },
.aarch64 => { .aarch64 => {
{ {
// TODO relax if possible // TODO relax if possible
const pages = try aarch64.calcNumberOfPages(@intCast(sect.addr), @intCast(dyld_private_addr)); const pages = try aarch64.calcNumberOfPages(@intCast(sect.addr), @intCast(dyld_private_addr));
try writer.writeInt(u32, aarch64.Instruction.adrp(.x17, pages).toU32(), .little); try bw.writeInt(u32, aarch64.Instruction.adrp(.x17, pages).toU32(), .little);
const off: u12 = @truncate(dyld_private_addr); const off: u12 = @truncate(dyld_private_addr);
try writer.writeInt(u32, aarch64.Instruction.add(.x17, .x17, off, false).toU32(), .little); try bw.writeInt(u32, aarch64.Instruction.add(.x17, .x17, off, false).toU32(), .little);
} }
try writer.writeInt(u32, aarch64.Instruction.stp( try bw.writeInt(u32, aarch64.Instruction.stp(
.x16, .x16,
.x17, .x17,
aarch64.Register.sp, aarch64.Register.sp,
@ -272,15 +258,15 @@ pub const StubsHelperSection = struct {
{ {
// TODO relax if possible // TODO relax if possible
const pages = try aarch64.calcNumberOfPages(@intCast(sect.addr + 12), @intCast(dyld_stub_binder_addr)); const pages = try aarch64.calcNumberOfPages(@intCast(sect.addr + 12), @intCast(dyld_stub_binder_addr));
try writer.writeInt(u32, aarch64.Instruction.adrp(.x16, pages).toU32(), .little); try bw.writeInt(u32, aarch64.Instruction.adrp(.x16, pages).toU32(), .little);
const off = try math.divExact(u12, @truncate(dyld_stub_binder_addr), 8); const off = try math.divExact(u12, @truncate(dyld_stub_binder_addr), 8);
try writer.writeInt(u32, aarch64.Instruction.ldr( try bw.writeInt(u32, aarch64.Instruction.ldr(
.x16, .x16,
.x16, .x16,
aarch64.Instruction.LoadStoreOffset.imm(off), aarch64.Instruction.LoadStoreOffset.imm(off),
).toU32(), .little); ).toU32(), .little);
} }
try writer.writeInt(u32, aarch64.Instruction.br(.x16).toU32(), .little); try bw.writeInt(u32, aarch64.Instruction.br(.x16).toU32(), .little);
}, },
else => unreachable, else => unreachable,
} }
@ -293,7 +279,7 @@ pub const LaSymbolPtrSection = struct {
return macho_file.stubs.symbols.items.len * @sizeOf(u64); return macho_file.stubs.symbols.items.len * @sizeOf(u64);
} }
pub fn write(laptr: LaSymbolPtrSection, macho_file: *MachO, writer: anytype) !void { pub fn write(laptr: LaSymbolPtrSection, macho_file: *MachO, bw: *Writer) !void {
const tracy = trace(@src()); const tracy = trace(@src());
defer tracy.end(); defer tracy.end();
_ = laptr; _ = laptr;
@ -304,12 +290,12 @@ pub const LaSymbolPtrSection = struct {
const sym = ref.getSymbol(macho_file).?; const sym = ref.getSymbol(macho_file).?;
if (sym.flags.weak) { if (sym.flags.weak) {
const value = sym.getAddress(.{ .stubs = false }, macho_file); const value = sym.getAddress(.{ .stubs = false }, macho_file);
try writer.writeInt(u64, @intCast(value), .little); try bw.writeInt(u64, @intCast(value), .little);
} else { } else {
const value = sect.addr + StubsHelperSection.preambleSize(cpu_arch) + const value = sect.addr + StubsHelperSection.preambleSize(cpu_arch) +
StubsHelperSection.entrySize(cpu_arch) * stub_helper_idx; StubsHelperSection.entrySize(cpu_arch) * stub_helper_idx;
stub_helper_idx += 1; stub_helper_idx += 1;
try writer.writeInt(u64, @intCast(value), .little); try bw.writeInt(u64, @intCast(value), .little);
} }
} }
} }
@ -343,48 +329,41 @@ pub const TlvPtrSection = struct {
return tlv.symbols.items.len * @sizeOf(u64); return tlv.symbols.items.len * @sizeOf(u64);
} }
pub fn write(tlv: TlvPtrSection, macho_file: *MachO, writer: anytype) !void { pub fn write(tlv: TlvPtrSection, macho_file: *MachO, bw: *Writer) !void {
const tracy = trace(@src()); const tracy = trace(@src());
defer tracy.end(); defer tracy.end();
for (tlv.symbols.items) |ref| { for (tlv.symbols.items) |ref| {
const sym = ref.getSymbol(macho_file).?; const sym = ref.getSymbol(macho_file).?;
if (sym.flags.import) { if (sym.flags.import) {
try writer.writeInt(u64, 0, .little); try bw.writeInt(u64, 0, .little);
} else { } else {
try writer.writeInt(u64, sym.getAddress(.{}, macho_file), .little); try bw.writeInt(u64, sym.getAddress(.{}, macho_file), .little);
} }
} }
} }
const FormatCtx = struct { pub fn fmt(tlv: TlvPtrSection, macho_file: *MachO) std.fmt.Formatter(Format, Format.print) {
tlv: TlvPtrSection,
macho_file: *MachO,
};
pub fn fmt(tlv: TlvPtrSection, macho_file: *MachO) std.fmt.Formatter(format2) {
return .{ .data = .{ .tlv = tlv, .macho_file = macho_file } }; return .{ .data = .{ .tlv = tlv, .macho_file = macho_file } };
} }
pub fn format2( const Format = struct {
ctx: FormatCtx, tlv: TlvPtrSection,
comptime unused_fmt_string: []const u8, macho_file: *MachO,
options: std.fmt.FormatOptions,
writer: anytype, pub fn print(f: Format, w: *Writer) Writer.Error!void {
) !void { for (f.tlv.symbols.items, 0..) |ref, i| {
_ = options; const symbol = ref.getSymbol(f.macho_file).?;
_ = unused_fmt_string; try w.print(" {d}@0x{x} => {f}@0x{x} ({s})\n", .{
for (ctx.tlv.symbols.items, 0..) |ref, i| {
const symbol = ref.getSymbol(ctx.macho_file).?;
try writer.print(" {d}@0x{x} => {d}@0x{x} ({s})\n", .{
i, i,
symbol.getTlvPtrAddress(ctx.macho_file), symbol.getTlvPtrAddress(f.macho_file),
ref, ref,
symbol.getAddress(.{}, ctx.macho_file), symbol.getAddress(.{}, f.macho_file),
symbol.getName(ctx.macho_file), symbol.getName(f.macho_file),
}); });
} }
} }
};
}; };
pub const ObjcStubsSection = struct { pub const ObjcStubsSection = struct {
@ -421,7 +400,7 @@ pub const ObjcStubsSection = struct {
return objc.symbols.items.len * entrySize(macho_file.getTarget().cpu.arch); return objc.symbols.items.len * entrySize(macho_file.getTarget().cpu.arch);
} }
pub fn write(objc: ObjcStubsSection, macho_file: *MachO, writer: anytype) !void { pub fn write(objc: ObjcStubsSection, macho_file: *MachO, bw: *Writer) !void {
const tracy = trace(@src()); const tracy = trace(@src());
defer tracy.end(); defer tracy.end();
@ -432,18 +411,18 @@ pub const ObjcStubsSection = struct {
const addr = objc.getAddress(@intCast(idx), macho_file); const addr = objc.getAddress(@intCast(idx), macho_file);
switch (macho_file.getTarget().cpu.arch) { switch (macho_file.getTarget().cpu.arch) {
.x86_64 => { .x86_64 => {
try writer.writeAll(&.{ 0x48, 0x8b, 0x35 }); try bw.writeAll(&.{ 0x48, 0x8b, 0x35 });
{ {
const target = sym.getObjcSelrefsAddress(macho_file); const target = sym.getObjcSelrefsAddress(macho_file);
const source = addr; const source = addr;
try writer.writeInt(i32, @intCast(target - source - 3 - 4), .little); try bw.writeInt(i32, @intCast(target - source - 3 - 4), .little);
} }
try writer.writeAll(&.{ 0xff, 0x25 }); try bw.writeAll(&.{ 0xff, 0x25 });
{ {
const target_sym = obj.getObjcMsgSendRef(macho_file).?.getSymbol(macho_file).?; const target_sym = obj.getObjcMsgSendRef(macho_file).?.getSymbol(macho_file).?;
const target = target_sym.getGotAddress(macho_file); const target = target_sym.getGotAddress(macho_file);
const source = addr + 7; const source = addr + 7;
try writer.writeInt(i32, @intCast(target - source - 2 - 4), .little); try bw.writeInt(i32, @intCast(target - source - 2 - 4), .little);
} }
}, },
.aarch64 => { .aarch64 => {
@ -451,9 +430,9 @@ pub const ObjcStubsSection = struct {
const target = sym.getObjcSelrefsAddress(macho_file); const target = sym.getObjcSelrefsAddress(macho_file);
const source = addr; const source = addr;
const pages = try aarch64.calcNumberOfPages(@intCast(source), @intCast(target)); const pages = try aarch64.calcNumberOfPages(@intCast(source), @intCast(target));
try writer.writeInt(u32, aarch64.Instruction.adrp(.x1, pages).toU32(), .little); try bw.writeInt(u32, aarch64.Instruction.adrp(.x1, pages).toU32(), .little);
const off = try math.divExact(u12, @truncate(target), 8); const off = try math.divExact(u12, @truncate(target), 8);
try writer.writeInt( try bw.writeInt(
u32, u32,
aarch64.Instruction.ldr(.x1, .x1, aarch64.Instruction.LoadStoreOffset.imm(off)).toU32(), aarch64.Instruction.ldr(.x1, .x1, aarch64.Instruction.LoadStoreOffset.imm(off)).toU32(),
.little, .little,
@ -464,52 +443,45 @@ pub const ObjcStubsSection = struct {
const target = target_sym.getGotAddress(macho_file); const target = target_sym.getGotAddress(macho_file);
const source = addr + 2 * @sizeOf(u32); const source = addr + 2 * @sizeOf(u32);
const pages = try aarch64.calcNumberOfPages(@intCast(source), @intCast(target)); const pages = try aarch64.calcNumberOfPages(@intCast(source), @intCast(target));
try writer.writeInt(u32, aarch64.Instruction.adrp(.x16, pages).toU32(), .little); try bw.writeInt(u32, aarch64.Instruction.adrp(.x16, pages).toU32(), .little);
const off = try math.divExact(u12, @truncate(target), 8); const off = try math.divExact(u12, @truncate(target), 8);
try writer.writeInt( try bw.writeInt(
u32, u32,
aarch64.Instruction.ldr(.x16, .x16, aarch64.Instruction.LoadStoreOffset.imm(off)).toU32(), aarch64.Instruction.ldr(.x16, .x16, aarch64.Instruction.LoadStoreOffset.imm(off)).toU32(),
.little, .little,
); );
} }
try writer.writeInt(u32, aarch64.Instruction.br(.x16).toU32(), .little); try bw.writeInt(u32, aarch64.Instruction.br(.x16).toU32(), .little);
try writer.writeInt(u32, aarch64.Instruction.brk(1).toU32(), .little); try bw.writeInt(u32, aarch64.Instruction.brk(1).toU32(), .little);
try writer.writeInt(u32, aarch64.Instruction.brk(1).toU32(), .little); try bw.writeInt(u32, aarch64.Instruction.brk(1).toU32(), .little);
try writer.writeInt(u32, aarch64.Instruction.brk(1).toU32(), .little); try bw.writeInt(u32, aarch64.Instruction.brk(1).toU32(), .little);
}, },
else => unreachable, else => unreachable,
} }
} }
} }
const FormatCtx = struct { pub fn fmt(objc: ObjcStubsSection, macho_file: *MachO) std.fmt.Formatter(Format, Format.print) {
objc: ObjcStubsSection,
macho_file: *MachO,
};
pub fn fmt(objc: ObjcStubsSection, macho_file: *MachO) std.fmt.Formatter(format2) {
return .{ .data = .{ .objc = objc, .macho_file = macho_file } }; return .{ .data = .{ .objc = objc, .macho_file = macho_file } };
} }
pub fn format2( const Format = struct {
ctx: FormatCtx, objc: ObjcStubsSection,
comptime unused_fmt_string: []const u8, macho_file: *MachO,
options: std.fmt.FormatOptions,
writer: anytype, pub fn print(f: Format, w: *Writer) Writer.Error!void {
) !void { for (f.objc.symbols.items, 0..) |ref, i| {
_ = options; const symbol = ref.getSymbol(f.macho_file).?;
_ = unused_fmt_string; try w.print(" {d}@0x{x} => {f}@0x{x} ({s})\n", .{
for (ctx.objc.symbols.items, 0..) |ref, i| {
const symbol = ref.getSymbol(ctx.macho_file).?;
try writer.print(" {d}@0x{x} => {d}@0x{x} ({s})\n", .{
i, i,
symbol.getObjcStubsAddress(ctx.macho_file), symbol.getObjcStubsAddress(f.macho_file),
ref, ref,
symbol.getAddress(.{}, ctx.macho_file), symbol.getAddress(.{}, f.macho_file),
symbol.getName(ctx.macho_file), symbol.getName(f.macho_file),
}); });
} }
} }
};
pub const Index = u32; pub const Index = u32;
}; };
@ -524,7 +496,7 @@ pub const Indsymtab = struct {
macho_file.dysymtab_cmd.nindirectsyms = ind.nsyms(macho_file); macho_file.dysymtab_cmd.nindirectsyms = ind.nsyms(macho_file);
} }
pub fn write(ind: Indsymtab, macho_file: *MachO, writer: anytype) !void { pub fn write(ind: Indsymtab, macho_file: *MachO, bw: *Writer) !void {
const tracy = trace(@src()); const tracy = trace(@src());
defer tracy.end(); defer tracy.end();
@ -533,21 +505,21 @@ pub const Indsymtab = struct {
for (macho_file.stubs.symbols.items) |ref| { for (macho_file.stubs.symbols.items) |ref| {
const sym = ref.getSymbol(macho_file).?; const sym = ref.getSymbol(macho_file).?;
if (sym.getOutputSymtabIndex(macho_file)) |idx| { if (sym.getOutputSymtabIndex(macho_file)) |idx| {
try writer.writeInt(u32, idx, .little); try bw.writeInt(u32, idx, .little);
} }
} }
for (macho_file.got.symbols.items) |ref| { for (macho_file.got.symbols.items) |ref| {
const sym = ref.getSymbol(macho_file).?; const sym = ref.getSymbol(macho_file).?;
if (sym.getOutputSymtabIndex(macho_file)) |idx| { if (sym.getOutputSymtabIndex(macho_file)) |idx| {
try writer.writeInt(u32, idx, .little); try bw.writeInt(u32, idx, .little);
} }
} }
for (macho_file.stubs.symbols.items) |ref| { for (macho_file.stubs.symbols.items) |ref| {
const sym = ref.getSymbol(macho_file).?; const sym = ref.getSymbol(macho_file).?;
if (sym.getOutputSymtabIndex(macho_file)) |idx| { if (sym.getOutputSymtabIndex(macho_file)) |idx| {
try writer.writeInt(u32, idx, .little); try bw.writeInt(u32, idx, .little);
} }
} }
} }
@ -601,7 +573,7 @@ pub const DataInCode = struct {
macho_file.data_in_code_cmd.datasize = math.cast(u32, dice.size()) orelse return error.Overflow; macho_file.data_in_code_cmd.datasize = math.cast(u32, dice.size()) orelse return error.Overflow;
} }
pub fn write(dice: DataInCode, macho_file: *MachO, writer: anytype) !void { pub fn write(dice: DataInCode, macho_file: *MachO, bw: *Writer) !void {
const base_address = if (!macho_file.base.isRelocatable()) const base_address = if (!macho_file.base.isRelocatable())
macho_file.getTextSegment().vmaddr macho_file.getTextSegment().vmaddr
else else
@ -609,7 +581,7 @@ pub const DataInCode = struct {
for (dice.entries.items) |entry| { for (dice.entries.items) |entry| {
const atom_address = entry.atom_ref.getAtom(macho_file).?.getAddress(macho_file); const atom_address = entry.atom_ref.getAtom(macho_file).?.getAddress(macho_file);
const offset = atom_address + entry.offset - base_address; const offset = atom_address + entry.offset - base_address;
try writer.writeStruct(macho.data_in_code_entry{ try bw.writeStruct(macho.data_in_code_entry{
.offset = @intCast(offset), .offset = @intCast(offset),
.length = entry.length, .length = entry.length,
.kind = entry.kind, .kind = entry.kind,
@ -625,13 +597,14 @@ pub const DataInCode = struct {
}; };
}; };
const std = @import("std");
const aarch64 = @import("../aarch64.zig"); const aarch64 = @import("../aarch64.zig");
const assert = std.debug.assert; const assert = std.debug.assert;
const macho = std.macho; const macho = std.macho;
const math = std.math; const math = std.math;
const std = @import("std");
const trace = @import("../../tracy.zig").trace;
const Allocator = std.mem.Allocator; const Allocator = std.mem.Allocator;
const Writer = std.io.Writer;
const trace = @import("../../tracy.zig").trace;
const MachO = @import("../MachO.zig"); const MachO = @import("../MachO.zig");
const Symbol = @import("Symbol.zig"); const Symbol = @import("Symbol.zig");