diff options
| author | Andrew Kelley <andrew@ziglang.org> | 2025-07-01 18:14:45 -0700 |
|---|---|---|
| committer | Andrew Kelley <andrew@ziglang.org> | 2025-07-07 22:43:52 -0700 |
| commit | c8fcd2ff2c032b2de8cc1a57e075552d1cab35df (patch) | |
| tree | 7155f58049ecd0e948533f6b64cba1553dd33ae2 | |
| parent | f71d97e4cbb0e56213cb76657ad6c9edf6134868 (diff) | |
| download | zig-c8fcd2ff2c032b2de8cc1a57e075552d1cab35df.tar.gz zig-c8fcd2ff2c032b2de8cc1a57e075552d1cab35df.zip | |
MachO: update to new std.io APIs
25 files changed, 1580 insertions, 1649 deletions
diff --git a/lib/compiler/aro/aro/Diagnostics.zig b/lib/compiler/aro/aro/Diagnostics.zig index 04c92d58a8..5b7be39617 100644 --- a/lib/compiler/aro/aro/Diagnostics.zig +++ b/lib/compiler/aro/aro/Diagnostics.zig @@ -444,7 +444,7 @@ pub fn renderMessage(comp: *Compilation, m: anytype, msg: Message) void { printRt(m, prop.msg, .{"{s}"}, .{&str}); } else { 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}); } }, @@ -525,13 +525,13 @@ fn tagKind(d: *Diagnostics, tag: Tag, langopts: LangOpts) Kind { } const MsgWriter = struct { - w: std.io.BufferedWriter(4096, std.fs.File.Writer), + w: *std.fs.File.Writer, 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(); return .{ - .w = std.io.bufferedWriter(std.fs.File.stderr().deprecatedWriter()), + .w = std.fs.stderr().writer(buffer), .config = config, }; } diff --git a/src/deprecated.zig b/src/deprecated.zig new file mode 100644 index 0000000000..1f7d5c8c25 --- /dev/null +++ b/src/deprecated.zig @@ -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. + } + } +} diff --git a/src/link/MachO.zig b/src/link/MachO.zig index 7443435264..1ac91eb12e 100644 --- a/src/link/MachO.zig +++ b/src/link/MachO.zig @@ -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 }, codesig_cmd: macho.linkedit_data_command = .{ .cmd = .CODE_SIGNATURE }, -pagezero_seg_index: ?u8 = null, -text_seg_index: ?u8 = null, -linkedit_seg_index: ?u8 = null, +pagezero_seg_index: ?u4 = null, +text_seg_index: ?u4 = null, +linkedit_seg_index: ?u4 = null, text_sect_index: ?u8 = null, data_sect_index: ?u8 = null, got_sect_index: ?u8 = null, @@ -76,10 +76,10 @@ unwind_info: UnwindInfo = .{}, data_in_code: DataInCode = .{}, /// Tracked loadable segments during incremental linking. -zig_text_seg_index: ?u8 = null, -zig_const_seg_index: ?u8 = null, -zig_data_seg_index: ?u8 = null, -zig_bss_seg_index: ?u8 = null, +zig_text_seg_index: ?u4 = null, +zig_const_seg_index: ?u4 = null, +zig_data_seg_index: ?u4 = null, +zig_bss_seg_index: ?u4 = null, /// Tracked section headers with incremental updates to Zig object. zig_text_sect_index: ?u8 = null, @@ -543,7 +543,7 @@ pub fn flush( self.allocateSyntheticSymbols(); 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 @@ -591,6 +591,7 @@ pub fn flush( error.NoSpaceLeft => unreachable, error.OutOfMemory => return error.OutOfMemory, error.LinkFailure => return error.LinkFailure, + else => unreachable, }; try self.writeHeader(ncmds, sizeofcmds); 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(@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| { try argv.append(try std.fmt.allocPrint(arena, "{d}.{d}", .{ ver.major, ver.minor })); } 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| { @@ -863,7 +864,7 @@ pub fn classifyInputFile(self: *MachO, input: link.Input) !void { const path, const file = input.pathAndFile().?; // 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); var buffer: [Archive.SARMAG]u8 = undefined; @@ -1074,7 +1075,7 @@ fn accessLibPath( for (&[_][]const u8{ ".tbd", ".dylib", "" }) |ext| { 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)); fs.cwd().access(test_path.items, .{}) catch |err| switch (err) { error.FileNotFound => continue, @@ -1097,7 +1098,7 @@ fn accessFrameworkPath( for (&[_][]const u8{ ".tbd", ".dylib", "" }) |ext| { 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, name, name, @@ -1178,9 +1179,9 @@ fn parseDependentDylibs(self: *MachO) !void { for (&[_][]const u8{ ".tbd", ".dylib", "" }) |ext| { test_path.clearRetainingCapacity(); 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 { - 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)); 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 file = self.getFile(ref.file).?; 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) { @@ -2131,7 +2132,7 @@ fn initSegments(self: *MachO) !void { 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); for (entries.items, 0..) |entry, i| { backlinks[entry.index] = @intCast(i); @@ -2145,7 +2146,7 @@ fn initSegments(self: *MachO) !void { self.segments.appendAssumeCapacity(segments[sorted.index]); } - for (&[_]*?u8{ + for (&[_]*?u4{ &self.pagezero_seg_index, &self.text_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| { const segname = header.segName(); 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); try self.segments.append(gpa, .{ .cmdsize = @sizeOf(macho.segment_command_64), @@ -2526,10 +2527,8 @@ fn writeThunkWorker(self: *MachO, thunk: Thunk) void { const doWork = struct { fn doWork(th: Thunk, buffer: []u8, macho_file: *MachO) !void { - const off = try macho_file.cast(usize, th.value); - const size = th.size(); - var stream = std.io.fixedBufferStream(buffer[off..][0..size]); - try th.write(macho_file, stream.writer()); + var bw: Writer = .fixed(buffer[try macho_file.cast(usize, th.value)..][0..th.size()]); + try th.write(macho_file, &bw); } }.doWork; 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 { fn doWork(macho_file: *MachO, tag: Tag, buffer: []u8) !void { - var stream = std.io.fixedBufferStream(buffer); + var bw: Writer = .fixed(buffer); switch (tag) { .eh_frame => eh_frame.write(macho_file, buffer), - .unwind_info => try macho_file.unwind_info.write(macho_file, buffer), - .got => try macho_file.got.write(macho_file, stream.writer()), - .stubs => try macho_file.stubs.write(macho_file, stream.writer()), - .la_symbol_ptr => try macho_file.la_symbol_ptr.write(macho_file, stream.writer()), - .tlv_ptr => try macho_file.tlv_ptr.write(macho_file, stream.writer()), - .objc_stubs => try macho_file.objc_stubs.write(macho_file, stream.writer()), + .unwind_info => try macho_file.unwind_info.write(macho_file, &bw), + .got => try macho_file.got.write(macho_file, &bw), + .stubs => try macho_file.stubs.write(macho_file, &bw), + .la_symbol_ptr => try macho_file.la_symbol_ptr.write(macho_file, &bw), + .tlv_ptr => try macho_file.tlv_ptr.write(macho_file, &bw), + .objc_stubs => try macho_file.objc_stubs.write(macho_file, &bw), } } }.doWork; @@ -2605,8 +2604,8 @@ fn updateLazyBindSizeWorker(self: *MachO) void { try macho_file.lazy_bind_section.updateSize(macho_file); const sect_id = macho_file.stubs_helper_sect_index.?; const out = &macho_file.sections.items(.out)[sect_id]; - var stream = std.io.fixedBufferStream(out.items); - try macho_file.stubs_helper.write(macho_file, stream.writer()); + var bw: Writer = .fixed(out.items); + try macho_file.stubs_helper.write(macho_file, &bw); } }.doWork; doWork(self) catch |err| @@ -2665,46 +2664,49 @@ fn writeDyldInfo(self: *MachO) !void { needed_size += cmd.lazy_bind_size; needed_size += cmd.export_size; - const buffer = try gpa.alloc(u8, needed_size); - defer gpa.free(buffer); - @memset(buffer, 0); - - var stream = std.io.fixedBufferStream(buffer); - const writer = stream.writer(); + var bw: Writer = .fixed(try gpa.alloc(u8, needed_size)); + defer gpa.free(bw.buffer); + @memset(bw.buffer, 0); - try self.rebase_section.write(writer); - try stream.seekTo(cmd.bind_off - base_off); - try self.bind_section.write(writer); - try stream.seekTo(cmd.weak_bind_off - base_off); - try self.weak_bind_section.write(writer); - try stream.seekTo(cmd.lazy_bind_off - base_off); - try self.lazy_bind_section.write(writer); - try stream.seekTo(cmd.export_off - base_off); - try self.export_trie.write(writer); - try self.pwriteAll(buffer, cmd.rebase_off); + try self.rebase_section.write(&bw); + bw.end = cmd.bind_off - base_off; + try self.bind_section.write(&bw); + bw.end = cmd.weak_bind_off - base_off; + try self.weak_bind_section.write(&bw); + bw.end = cmd.lazy_bind_off - base_off; + try self.lazy_bind_section.write(&bw); + bw.end = cmd.export_off - base_off; + try self.export_trie.write(&bw); + try self.pwriteAll(bw.buffer, cmd.rebase_off); } -pub fn writeDataInCode(self: *MachO) !void { +pub fn writeDataInCode(self: *MachO) link.File.FlushError!void { const tracy = trace(@src()); defer tracy.end(); const gpa = self.base.comp.gpa; const cmd = self.data_in_code_cmd; - var buffer = try std.ArrayList(u8).initCapacity(gpa, self.data_in_code.size()); - defer buffer.deinit(); - try self.data_in_code.write(self, buffer.writer()); - try self.pwriteAll(buffer.items, cmd.dataoff); + + var bw: Writer = .fixed(try gpa.alloc(u8, self.data_in_code.size())); + defer gpa.free(bw.buffer); + + 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 { const tracy = trace(@src()); defer tracy.end(); + const gpa = self.base.comp.gpa; const cmd = self.dysymtab_cmd; - const needed_size = cmd.nindirectsyms * @sizeOf(u32); - var buffer = try std.ArrayList(u8).initCapacity(gpa, needed_size); - defer buffer.deinit(); - try self.indsymtab.write(self, buffer.writer()); - try self.pwriteAll(buffer.items, cmd.indirectsymoff); + + var bw: Writer = .fixed(try gpa.alloc(u8, @sizeOf(u32) * cmd.nindirectsyms)); + defer gpa.free(bw.buffer); + + 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 { @@ -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 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); - const writer = stream.writer(); + var bw: Writer = .fixed(try gpa.alloc(u8, try load_commands.calcLoadCommandsSize(self, false))); + defer gpa.free(bw.buffer); var ncmds: usize = 0; @@ -2831,26 +2830,26 @@ fn writeLoadCommands(self: *MachO) !struct { usize, usize, u64 } { const slice = self.sections.slice(); var sect_id: usize = 0; for (self.segments.items) |seg| { - try writer.writeStruct(seg); + try bw.writeStruct(seg); for (slice.items(.header)[sect_id..][0..seg.nsects]) |header| { - try writer.writeStruct(header); + try bw.writeStruct(header); } sect_id += seg.nsects; } ncmds += self.segments.items.len; } - try writer.writeStruct(self.dyld_info_cmd); + try bw.writeStruct(self.dyld_info_cmd); ncmds += 1; - try writer.writeStruct(self.function_starts_cmd); + try bw.writeStruct(self.function_starts_cmd); ncmds += 1; - try writer.writeStruct(self.data_in_code_cmd); + try bw.writeStruct(self.data_in_code_cmd); ncmds += 1; - try writer.writeStruct(self.symtab_cmd); + try bw.writeStruct(self.symtab_cmd); ncmds += 1; - try writer.writeStruct(self.dysymtab_cmd); + try bw.writeStruct(self.dysymtab_cmd); ncmds += 1; - try load_commands.writeDylinkerLC(writer); + try load_commands.writeDylinkerLC(&bw); ncmds += 1; if (self.getInternalObject()) |obj| { @@ -2861,7 +2860,7 @@ fn writeLoadCommands(self: *MachO) !struct { usize, usize, u64 } { 0 else @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, .stacksize = self.base.stack_size, }); @@ -2870,35 +2869,35 @@ fn writeLoadCommands(self: *MachO) !struct { usize, usize, u64 } { } if (self.base.isDynLib()) { - try load_commands.writeDylibIdLC(self, writer); + try load_commands.writeDylibIdLC(self, &bw); ncmds += 1; } for (self.rpath_list) |rpath| { - try load_commands.writeRpathLC(rpath, writer); + try load_commands.writeRpathLC(&bw, rpath); ncmds += 1; } if (comp.config.any_sanitize_thread) { const path = try comp.tsan_lib.?.full_object_path.toString(gpa); defer gpa.free(path); const rpath = std.fs.path.dirname(path) orelse "."; - try load_commands.writeRpathLC(rpath, writer); + try load_commands.writeRpathLC(&bw, rpath); ncmds += 1; } - try writer.writeStruct(macho.source_version_command{ .version = 0 }); + try bw.writeStruct(macho.source_version_command{ .version = 0 }); ncmds += 1; 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; } else { - try load_commands.writeVersionMinLC(self.platform, self.sdk_version, writer); + try load_commands.writeVersionMinLC(&bw, self.platform, self.sdk_version); ncmds += 1; } - const uuid_cmd_offset = @sizeOf(macho.mach_header_64) + stream.pos; - try writer.writeStruct(self.uuid_cmd); + const uuid_cmd_offset = @sizeOf(macho.mach_header_64) + bw.count; + try bw.writeStruct(self.uuid_cmd); ncmds += 1; for (self.dylibs.items) |index| { @@ -2916,20 +2915,19 @@ fn writeLoadCommands(self: *MachO) !struct { usize, usize, u64 } { .timestamp = dylib_id.timestamp, .current_version = dylib_id.current_version, .compatibility_version = dylib_id.compatibility_version, - }, writer); + }, &bw); ncmds += 1; } if (self.requiresCodeSig()) { - try writer.writeStruct(self.codesig_cmd); + try bw.writeStruct(self.codesig_cmd); ncmds += 1; } - assert(stream.pos == needed_size); - - try self.pwriteAll(buffer, @sizeOf(macho.mach_header_64)); + assert(bw.end == bw.buffer.len); + try self.pwriteAll(bw.buffer, @sizeOf(macho.mach_header_64)); - return .{ ncmds, buffer.len, uuid_cmd_offset }; + return .{ ncmds, bw.end, uuid_cmd_offset }; } 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 { + const gpa = self.base.comp.gpa; const seg = self.getTextSegment(); const offset = self.codesig_cmd.dataoff; - var buffer = std.ArrayList(u8).init(self.base.comp.gpa); - defer buffer.deinit(); - try buffer.ensureTotalCapacityPrecise(code_sig.size()); + var bw: Writer = .fixed(try gpa.alloc(u8, code_sig.size())); + defer gpa.free(bw.buffer); try code_sig.writeAdhocSignature(self, .{ .file = self.base.file.?, .exec_seg_base = seg.fileoff, .exec_seg_limit = seg.filesize, .file_size = offset, .dylib = self.base.isDynLib(), - }, buffer.writer()); - assert(buffer.items.len == code_sig.size()); + }, &bw); log.debug("writing code signature from 0x{x} to 0x{x}", .{ 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( @@ -3341,7 +3339,7 @@ fn initMetadata(self: *MachO, options: InitMetadataOptions) !void { } 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 seg = macho_file.segments.items[seg_id]; sect.addr = seg.vmaddr; @@ -3600,7 +3598,7 @@ inline fn requiresThunks(self: MachO) bool { } pub fn isZigSegment(self: MachO, seg_id: u8) bool { - inline for (&[_]?u8{ + inline for (&[_]?u4{ self.zig_text_seg_index, self.zig_const_seg_index, self.zig_data_seg_index, @@ -3648,9 +3646,9 @@ pub fn addSegment(self: *MachO, name: []const u8, opts: struct { fileoff: u64 = 0, filesize: u64 = 0, prot: macho.vm_prot_t = macho.PROT.NONE, -}) error{OutOfMemory}!u8 { +}) error{OutOfMemory}!u4 { 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, .{ .segname = makeStaticString(name), .vmaddr = opts.vmaddr, @@ -3700,9 +3698,9 @@ pub fn makeStaticString(bytes: []const u8) [16]u8 { 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| { - 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; } @@ -3791,7 +3789,7 @@ pub fn reportParseError2( const diags = &self.base.comp.link_diags; var err = try diags.addErrorWithNotes(1); 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( @@ -3806,7 +3804,7 @@ fn reportMissingDependencyError( var err = try diags.addErrorWithNotes(2 + checked_paths.len); try err.addMsg(format, args); 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| { err.addNote("tried {s}", .{p}); } @@ -3823,7 +3821,7 @@ fn reportDependencyError( var err = try diags.addErrorWithNotes(2); try err.addMsg(format, args); 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 { @@ -3853,12 +3851,12 @@ fn reportDuplicates(self: *MachO) error{ HasDuplicates, OutOfMemory }!void { var err = try diags.addErrorWithNotes(nnotes + 1); 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; while (inote < @min(notes.items.len, max_notes)) : (inote += 1) { 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) { @@ -3900,35 +3898,28 @@ pub fn ptraceDetach(self: *MachO, pid: std.posix.pid_t) !void { 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 }; } -fn fmtDumpState( - self: *MachO, - comptime unused_fmt_string: []const u8, - options: std.fmt.FormatOptions, - writer: anytype, -) !void { - _ = options; - _ = unused_fmt_string; +fn fmtDumpState(self: *MachO, w: *Writer) Writer.Error!void { if (self.getZigObject()) |zo| { - try writer.print("zig_object({d}) : {s}\n", .{ zo.index, zo.basename }); - try writer.print("{}{}\n", .{ + try w.print("zig_object({d}) : {s}\n", .{ zo.index, zo.basename }); + try w.print("{f}{f}\n", .{ zo.fmtAtoms(self), zo.fmtSymtab(self), }); } for (self.objects.items) |index| { const object = self.getFile(index).?.object; - try writer.print("object({d}) : {} : has_debug({})", .{ + try w.print("object({d}) : {f} : has_debug({})", .{ index, object.fmtPath(), object.hasDebugInfo(), }); - if (!object.alive) try writer.writeAll(" : ([*])"); - try writer.writeByte('\n'); - try writer.print("{}{}{}{}{}\n", .{ + if (!object.alive) try w.writeAll(" : ([*])"); + try w.writeByte('\n'); + try w.print("{f}{f}{f}{f}{f}\n", .{ object.fmtAtoms(self), object.fmtCies(self), object.fmtFdes(self), @@ -3938,48 +3929,41 @@ fn fmtDumpState( } for (self.dylibs.items) |index| { const dylib = self.getFile(index).?.dylib; - try writer.print("dylib({d}) : {} : needed({}) : weak({})", .{ + try w.print("dylib({d}) : {f} : needed({}) : weak({})", .{ index, @as(Path, dylib.path), dylib.needed, dylib.weak, }); - if (!dylib.isAlive(self)) try writer.writeAll(" : ([*])"); - try writer.writeByte('\n'); - try writer.print("{}\n", .{dylib.fmtSymtab(self)}); + if (!dylib.isAlive(self)) try w.writeAll(" : ([*])"); + try w.writeByte('\n'); + try w.print("{f}\n", .{dylib.fmtSymtab(self)}); } if (self.getInternalObject()) |internal| { - try writer.print("internal({d}) : internal\n", .{internal.index}); - try writer.print("{}{}\n", .{ internal.fmtAtoms(self), internal.fmtSymtab(self) }); + try w.print("internal({d}) : internal\n", .{internal.index}); + 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| { - 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 writer.print("objc_stubs\n{}\n", .{self.objc_stubs.fmt(self)}); - try writer.print("got\n{}\n", .{self.got.fmt(self)}); - try writer.print("tlv_ptr\n{}\n", .{self.tlv_ptr.fmt(self)}); - try writer.writeByte('\n'); - try writer.print("sections\n{}\n", .{self.fmtSections()}); - try writer.print("segments\n{}\n", .{self.fmtSegments()}); + try w.print("stubs\n{f}\n", .{self.stubs.fmt(self)}); + try w.print("objc_stubs\n{f}\n", .{self.objc_stubs.fmt(self)}); + try w.print("got\n{f}\n", .{self.got.fmt(self)}); + try w.print("tlv_ptr\n{f}\n", .{self.tlv_ptr.fmt(self)}); + try w.writeByte('\n'); + try w.print("sections\n{f}\n", .{self.fmtSections()}); + 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 }; } -fn formatSections( - self: *MachO, - comptime unused_fmt_string: []const u8, - options: std.fmt.FormatOptions, - writer: anytype, -) !void { - _ = options; - _ = unused_fmt_string; +fn formatSections(self: *MachO, w: *Writer) Writer.Error!void { const slice = self.sections.slice(); 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", .{ 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 }; } -fn formatSegments( - self: *MachO, - comptime unused_fmt_string: []const u8, - options: std.fmt.FormatOptions, - writer: anytype, -) !void { - _ = options; - _ = unused_fmt_string; +fn formatSegments(self: *MachO, w: *Writer) Writer.Error!void { 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, 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 }; } -fn formatSectType( - tt: u8, - comptime unused_fmt_string: []const u8, - options: std.fmt.FormatOptions, - writer: anytype, -) !void { - _ = options; - _ = unused_fmt_string; +fn formatSectType(tt: u8, w: *Writer) Writer.Error!void { const name = switch (tt) { macho.S_REGULAR => "REGULAR", macho.S_ZEROFILL => "ZEROFILL", @@ -4044,9 +4014,9 @@ fn formatSectType( macho.S_THREAD_LOCAL_VARIABLE_POINTERS => "THREAD_LOCAL_VARIABLE_POINTERS", macho.S_THREAD_LOCAL_INIT_FUNCTION_POINTERS => "THREAD_LOCAL_INIT_FUNCTION_POINTERS", 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) { @@ -4058,7 +4028,7 @@ const default_entry_symbol_name = "_main"; const Section = struct { header: macho.section_64, - segment_id: u8, + segment_id: u4, atoms: std.ArrayListUnmanaged(Ref) = .empty, free_list: std.ArrayListUnmanaged(Atom.Index) = .empty, last_atom_index: Atom.Index = 0, @@ -4279,28 +4249,21 @@ pub const Platform = struct { 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 } }; } - const FmtCtx = struct { + const Format = struct { platform: Platform, cpu_arch: std.Target.Cpu.Arch, - }; - pub fn formatTarget( - ctx: FmtCtx, - comptime unused_fmt_string: []const u8, - options: std.fmt.FormatOptions, - 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)}); + pub fn target(f: Format, w: *Writer) Writer.Error!void { + try w.print("{s}-{s}", .{ @tagName(f.cpu_arch), @tagName(f.platform.os_tag) }); + if (f.platform.abi != .none) { + try w.print("-{s}", .{@tagName(f.platform.abi)}); + } } - } + }; /// Caller owns the memory. 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. fn readSdkVersionFromSettings(arena: Allocator, dir: []const u8) ![]const u8 { 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, .{}); if (parsed.value.object.get("MinimalDisplayName")) |ver| return ver.string; return error.SdkVersionFailure; @@ -4406,7 +4369,7 @@ fn parseSdkVersion(raw: []const u8) ?std.SemanticVersion { }; const parseNext = struct { - fn parseNext(it: anytype) ?u16 { + fn parseNext(it: *std.mem.SplitIterator(u8, .any)) ?u16 { const nn = it.next() orelse return null; return std.fmt.parseInt(u16, nn, 10) catch null; } @@ -4507,15 +4470,9 @@ pub const Ref = struct { }; } - pub fn format( - ref: Ref, - comptime unused_fmt_string: []const u8, - options: std.fmt.FormatOptions, - writer: anytype, - ) !void { - _ = unused_fmt_string; - _ = options; - try writer.print("%{d} in file({d})", .{ ref.index, ref.file }); + pub fn format(ref: Ref, bw: *Writer, comptime unused_fmt_string: []const u8) Writer.Error!void { + comptime assert(unused_fmt_string.len == 0); + try bw.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); 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 { const comp = macho_file.base.comp; const diags = &comp.link_diags; - macho_file.base.file.?.pwriteAll(bytes, offset) catch |err| { - return diags.fail("failed to write: {s}", .{@errorName(err)}); + var fw = macho_file.base.file.?.writer(); + 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 mem = std.mem; const meta = std.meta; +const Writer = std.io.Writer; const aarch64 = @import("../arch/aarch64/bits.zig"); const bind = @import("MachO/dyld_info/bind.zig"); diff --git a/src/link/MachO/Archive.zig b/src/link/MachO/Archive.zig index e7403c37d4..f074d740a7 100644 --- a/src/link/MachO/Archive.zig +++ b/src/link/MachO/Archive.zig @@ -71,53 +71,29 @@ pub fn unpack(self: *Archive, macho_file: *MachO, path: Path, handle_index: File .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); } } pub fn writeHeader( + bw: *Writer, object_name: []const u8, object_size: usize, format: Format, - writer: anytype, -) !void { - var hdr: ar_hdr = .{ - .ar_name = undefined, - .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; - } +) Writer.Error!void { + var hdr: ar_hdr = undefined; + @memset(mem.asBytes(&hdr), ' '); + inline for (@typeInfo(ar_hdr).@"struct".fields) |field| @field(hdr, field.name)[0] = '0'; @memcpy(&hdr.ar_fmag, ARFMAG); - 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; - - { - var stream = std.io.fixedBufferStream(&hdr.ar_name); - stream.writer().print("#1/{d}", .{object_name_len}) catch unreachable; - } - { - 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); - } + _ = std.fmt.bufPrint(&hdr.ar_size, "{d}", .{total_object_size}) catch unreachable; + try bw.writeStruct(hdr); + try bw.writeAll(object_name); + try bw.splatByteAll(0, object_name_len - object_name.len); } // 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); } - 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); // Header - try writeHeader(SYMDEF, ar.size(format), format, writer); + try writeHeader(bw, SYMDEF, ar.size(format), format); // 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 for (ar.entries.items) |entry| { const file_off = switch (macho_file.getFile(entry.file).?) { @@ -215,47 +191,37 @@ pub const ArSymtab = struct { else => unreachable, }; // Name offset - try writeInt(format, entry.off, writer); + try writeInt(bw, format, entry.off); // File offset - try writeInt(format, file_off, writer); + try writeInt(bw, format, file_off); } // Strtab size const strtab_size = mem.alignForward(usize, ar.strtab.buffer.items.len, ptr_width); - const padding = strtab_size - ar.strtab.buffer.items.len; - try writeInt(format, strtab_size, writer); + try writeInt(bw, format, strtab_size); // Strtab - try writer.writeAll(ar.strtab.buffer.items); - if (padding > 0) { - try writer.writeByteNTimes(0, padding); - } + try bw.writeAll(ar.strtab.buffer.items); + try bw.splatByteAll(0, strtab_size - ar.strtab.buffer.items.len); } - const FormatContext = struct { + const PrintFormat = struct { ar: ArSymtab, macho_file: *MachO, + + fn default(f: PrintFormat, bw: *Writer) Writer.Error!void { + const ar = f.ar; + const macho_file = f.macho_file; + for (ar.entries.items, 0..) |entry, i| { + const name = ar.strtab.getAssumeExists(entry.off); + const file = macho_file.getFile(entry.file).?; + 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(format2) { + pub fn fmt(ar: ArSymtab, macho_file: *MachO) std.fmt.Formatter(PrintFormat, PrintFormat.default) { return .{ .data = .{ .ar = ar, .macho_file = 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| { - const name = ar.strtab.getAssumeExists(entry.off); - const file = macho_file.getFile(entry.file).?; - try writer.print(" {d}: {s} in file({d})({})\n", .{ i, name, entry.file, file.fmtPath() }); - } - } - const Entry = struct { /// Symbol name offset off: u32, @@ -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) { - .p32 => try writer.writeInt(u32, std.math.cast(u32, value) orelse return error.Overflow, .little), - .p64 => try writer.writeInt(u64, value, .little), + .p32 => try bw.writeInt(u32, std.math.cast(u32, value) orelse return error.Overflow, .little), + .p64 => try bw.writeInt(u64, value, .little), } } @@ -304,8 +270,9 @@ const log = std.log.scoped(.link); const macho = std.macho; const mem = std.mem; const std = @import("std"); -const Allocator = mem.Allocator; +const Allocator = std.mem.Allocator; const Path = std.Build.Cache.Path; +const Writer = std.io.Writer; const Archive = @This(); const File = @import("file.zig").File; diff --git a/src/link/MachO/Atom.zig b/src/link/MachO/Atom.zig index baa9e6172c..76f48ed672 100644 --- a/src/link/MachO/Atom.zig +++ b/src/link/MachO/Atom.zig @@ -580,8 +580,9 @@ pub fn resolveRelocs(self: Atom, macho_file: *MachO, buffer: []u8) !void { relocs_log.debug("{x}: {s}", .{ self.value, name }); + var bw: Writer = .fixed(buffer); + var has_error = false; - var stream = std.io.fixedBufferStream(buffer); var i: usize = 0; while (i < relocs.len) : (i += 1) { const rel = relocs[i]; @@ -592,30 +593,28 @@ pub fn resolveRelocs(self: Atom, macho_file: *MachO, buffer: []u8) !void { if (rel.getTargetSymbol(self, macho_file).getFile(macho_file) == null) continue; } - try stream.seekTo(rel_offset); - self.resolveRelocInner(rel, subtractor, buffer, macho_file, stream.writer()) catch |err| { - switch (err) { - error.RelaxFail => { - const target = switch (rel.tag) { - .@"extern" => rel.getTargetSymbol(self, macho_file).getName(macho_file), - .local => rel.getTargetAtom(self, macho_file).getName(macho_file), - }; - try macho_file.reportParseError2( - file.getIndex(), - "{s}: 0x{x}: 0x{x}: failed to relax relocation: type {}, target {s}", - .{ - name, - self.getAddress(macho_file), - rel.offset, - rel.fmtPretty(macho_file.getTarget().cpu.arch), - target, - }, - ); - has_error = true; - }, - error.RelaxFailUnexpectedInstruction => has_error = true, - else => |e| return e, - } + bw.end = std.math.cast(usize, rel_offset) orelse return error.Overflow; + self.resolveRelocInner(rel, subtractor, buffer, macho_file, &bw) catch |err| switch (err) { + error.RelaxFail => { + const target = switch (rel.tag) { + .@"extern" => rel.getTargetSymbol(self, macho_file).getName(macho_file), + .local => rel.getTargetAtom(self, macho_file).getName(macho_file), + }; + try macho_file.reportParseError2( + file.getIndex(), + "{s}: 0x{x}: 0x{x}: failed to relax relocation: type {f}, target {s}", + .{ + name, + self.getAddress(macho_file), + rel.offset, + rel.fmtPretty(macho_file.getTarget().cpu.arch), + target, + }, + ); + has_error = true; + }, + error.RelaxFailUnexpectedInstruction => has_error = true, + else => |e| return e, }; } @@ -638,8 +637,8 @@ fn resolveRelocInner( subtractor: ?Relocation, code: []u8, macho_file: *MachO, - writer: anytype, -) ResolveError!void { + bw: *Writer, +) Writer.Error!void { const t = &macho_file.base.comp.root_mod.resolved_target.result; const cpu_arch = t.cpu.arch; const rel_offset = math.cast(usize, rel.offset - self.off) orelse return error.Overflow; @@ -653,7 +652,7 @@ fn resolveRelocInner( const divExact = struct { fn divExact(atom: Atom, r: Relocation, num: u12, den: u12, ctx: *MachO) !u12 { 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), r.fmtPretty(ctx.getTarget().cpu.arch), r.offset, @@ -664,14 +663,14 @@ fn resolveRelocInner( }.divExact; switch (rel.tag) { - .local => relocs_log.debug(" {x}<+{d}>: {}: [=> {x}] atom({d})", .{ + .local => relocs_log.debug(" {x}<+{d}>: {f}: [=> {x}] atom({d})", .{ P, rel_offset, rel.fmtPretty(cpu_arch), S + A - SUB, 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, rel_offset, rel.fmtPretty(cpu_arch), @@ -690,14 +689,14 @@ fn resolveRelocInner( if (rel.tag == .@"extern") { const sym = rel.getTargetSymbol(self, macho_file); if (sym.isTlvInit(macho_file)) { - try writer.writeInt(u64, @intCast(S - TLS), .little); + try bw.writeInt(u64, @intCast(S - TLS), .little); 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) { - 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; }, @@ -705,7 +704,7 @@ fn resolveRelocInner( assert(rel.tag == .@"extern"); assert(rel.meta.length == 2); assert(rel.meta.pcrel); - try writer.writeInt(i32, @intCast(G + A - P), .little); + try bw.writeInt(i32, @intCast(G + A - P), .little); }, .branch => { @@ -714,7 +713,7 @@ fn resolveRelocInner( assert(rel.tag == .@"extern"); 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 => { const disp: i28 = math.cast(i28, S + A - P) orelse blk: { const thunk = self.getThunk(macho_file); @@ -732,10 +731,10 @@ fn resolveRelocInner( assert(rel.meta.length == 2); assert(rel.meta.pcrel); 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 { 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); if (sym.getSectionFlags().tlv_ptr) { 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 { 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 => { assert(rel.meta.length == 2); assert(rel.meta.pcrel); - try writer.writeInt(i32, @intCast(S + A - P), .little); + try bw.writeInt(i32, @intCast(S + A - P), .little); }, .page, @@ -808,7 +807,7 @@ fn resolveRelocInner( 2 => try divExact(self, rel, @truncate(target), 4, 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)), }, }; - 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) { .mov => { 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; }, else => |x| { 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.getAddress(macho_file), rel.offset, rel.fmtPretty(.x86_64), }); 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; }, } @@ -924,7 +923,7 @@ const x86_64 = struct { switch (old_inst.encoding.mnemonic) { .mov => { 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; }, else => return error.RelaxFail, @@ -938,11 +937,8 @@ const x86_64 = struct { } fn encode(insts: []const Instruction, code: []u8) !void { - var stream = std.io.fixedBufferStream(code); - const writer = stream.writer(); - for (insts) |inst| { - try inst.encode(writer, .{}); - } + var bw: Writer = .fixed(code); + for (insts) |inst| try inst.encode(&bw, .{}); } 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) { - .local => relocs_log.debug(" {}: [{x} => {d}({s},{s})] + {x}", .{ + .local => relocs_log.debug(" {f}: [{x} => {d}({s},{s})] + {x}", .{ rel.fmtPretty(cpu_arch), r_address, 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(), addend, }), - .@"extern" => relocs_log.debug(" {}: [{x} => {d}({s})] + {x}", .{ + .@"extern" => relocs_log.debug(" {f}: [{x} => {d}({s})] + {x}", .{ rel.fmtPretty(cpu_arch), r_address, r_symbolnum, @@ -1117,60 +1113,40 @@ pub fn writeRelocs(self: Atom, macho_file: *MachO, code: []u8, buffer: []macho.r assert(i == buffer.len); } -pub fn format( - 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) { +pub fn fmt(atom: Atom, macho_file: *MachO) std.fmt.Formatter(Format, Format.print) { return .{ .data = .{ .atom = atom, .macho_file = macho_file, } }; } -const FormatContext = struct { +const Format = struct { atom: Atom, macho_file: *MachO, -}; -fn format2( - ctx: FormatContext, - comptime unused_fmt_string: []const u8, - 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); - try writer.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.out_n_sect, atom.alignment, atom.size, - atom.getRelocs(macho_file).len, atom.getExtra(macho_file).thunk, - }); - if (!atom.isAlive()) try writer.writeAll(" : [*]"); - if (atom.getUnwindRecords(macho_file).len > 0) { - try writer.writeAll(" : unwind{ "); - const extra = atom.getExtra(macho_file); - for (atom.getUnwindRecords(macho_file), extra.unwind_index..) |index, i| { - const rec = file.object.getUnwindRecord(index); - try writer.print("{d}", .{index}); - if (!rec.alive) try writer.writeAll("([*])"); - if (i < extra.unwind_index + extra.unwind_count - 1) try writer.writeAll(", "); + fn print(f: Format, w: *Writer) Writer.Error!void { + const atom = f.atom; + const macho_file = f.macho_file; + const file = atom.getFile(macho_file); + 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.out_n_sect, atom.alignment, atom.size, + atom.getRelocs(macho_file).len, atom.getExtra(macho_file).thunk, + }); + if (!atom.isAlive()) try w.writeAll(" : [*]"); + if (atom.getUnwindRecords(macho_file).len > 0) { + try w.writeAll(" : unwind{ "); + const extra = atom.getExtra(macho_file); + for (atom.getUnwindRecords(macho_file), extra.unwind_index..) |index, i| { + const rec = file.object.getUnwindRecord(index); + try w.print("{d}", .{index}); + if (!rec.alive) try w.writeAll("([*])"); + if (i < extra.unwind_index + extra.unwind_count - 1) try w.writeAll(", "); + } + try w.writeAll(" }"); } - try writer.writeAll(" }"); } -} +}; pub const Index = u32; @@ -1205,19 +1181,20 @@ pub const Extra = struct { pub const Alignment = @import("../../InternPool.zig").Alignment; -const aarch64 = @import("../aarch64.zig"); +const std = @import("std"); const assert = std.debug.assert; const macho = std.macho; const math = std.math; const mem = std.mem; const log = std.log.scoped(.link); const relocs_log = std.log.scoped(.link_relocs); -const std = @import("std"); -const trace = @import("../../tracy.zig").trace; - +const Writer = std.io.Writer; const Allocator = mem.Allocator; -const Atom = @This(); 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 MachO = @import("../MachO.zig"); const Object = @import("Object.zig"); diff --git a/src/link/MachO/CodeSignature.zig b/src/link/MachO/CodeSignature.zig index c8a092eab6..be1ce1e8f7 100644 --- a/src/link/MachO/CodeSignature.zig +++ b/src/link/MachO/CodeSignature.zig @@ -247,7 +247,7 @@ pub fn deinit(self: *CodeSignature, allocator: Allocator) void { pub fn addEntitlements(self: *CodeSignature, allocator: Allocator, path: []const u8) !void { const file = try fs.cwd().openFile(path, .{}); defer file.close(); - const inner = try file.readToEndAlloc(allocator, std.math.maxInt(u32)); + const inner = try file.readToEndAlloc(allocator, .unlimited); self.entitlements = .{ .inner = inner }; } @@ -304,10 +304,11 @@ pub fn writeAdhocSignature( var hash: [hash_size]u8 = undefined; if (self.requirements) |*req| { - var buf = std.ArrayList(u8).init(allocator); - defer buf.deinit(); - try req.write(buf.writer()); - Sha256.hash(buf.items, &hash, .{}); + var aw: std.io.Writer.Allocating = .init(allocator); + defer aw.deinit(); + + try req.write(&aw.writer); + Sha256.hash(aw.getWritten(), &hash, .{}); self.code_directory.addSpecialHash(req.slotType(), hash); try blobs.append(.{ .requirements = req }); @@ -316,10 +317,11 @@ pub fn writeAdhocSignature( } if (self.entitlements) |*ents| { - var buf = std.ArrayList(u8).init(allocator); - defer buf.deinit(); - try ents.write(buf.writer()); - Sha256.hash(buf.items, &hash, .{}); + var aw: std.io.Writer.Allocating = .init(allocator); + defer aw.deinit(); + + try ents.write(&aw.writer); + Sha256.hash(aw.getWritten(), &hash, .{}); self.code_directory.addSpecialHash(ents.slotType(), hash); try blobs.append(.{ .entitlements = ents }); diff --git a/src/link/MachO/DebugSymbols.zig b/src/link/MachO/DebugSymbols.zig index eef3492b48..7e4389e09f 100644 --- a/src/link/MachO/DebugSymbols.zig +++ b/src/link/MachO/DebugSymbols.zig @@ -269,18 +269,14 @@ fn finalizeDwarfSegment(self: *DebugSymbols, macho_file: *MachO) void { fn writeLoadCommands(self: *DebugSymbols, macho_file: *MachO) !struct { usize, usize } { const gpa = self.allocator; - const needed_size = load_commands.calcLoadCommandsSizeDsym(macho_file, self); - const buffer = try gpa.alloc(u8, needed_size); - defer gpa.free(buffer); - - var stream = std.io.fixedBufferStream(buffer); - const writer = stream.writer(); + var bw: Writer = .fixed(try gpa.alloc(u8, load_commands.calcLoadCommandsSizeDsym(macho_file, self))); + defer gpa.free(bw.buffer); var ncmds: usize = 0; // UUID comes first presumably to speed up lookup by the consumer like lldb. @memcpy(&self.uuid_cmd.uuid, &macho_file.uuid_cmd.uuid); - try writer.writeStruct(self.uuid_cmd); + try bw.writeStruct(self.uuid_cmd); ncmds += 1; // Segment and section load commands @@ -293,11 +289,11 @@ fn writeLoadCommands(self: *DebugSymbols, macho_file: *MachO) !struct { usize, u var out_seg = seg; out_seg.fileoff = 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| { var out_header = header; out_header.offset = 0; - try writer.writeStruct(out_header); + try bw.writeStruct(out_header); } 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. sect_id = 0; for (self.segments.items) |seg| { - try writer.writeStruct(seg); + try bw.writeStruct(seg); for (self.sections.items[sect_id..][0..seg.nsects]) |header| { - try writer.writeStruct(header); + try bw.writeStruct(header); } sect_id += seg.nsects; } ncmds += self.segments.items.len; } - try writer.writeStruct(self.symtab_cmd); + try bw.writeStruct(self.symtab_cmd); ncmds += 1; - assert(stream.pos == needed_size); - - try self.file.?.pwriteAll(buffer, @sizeOf(macho.mach_header_64)); + assert(bw.end == bw.buffer.len); + try self.file.?.pwriteAll(bw.buffer, @sizeOf(macho.mach_header_64)); - return .{ ncmds, buffer.len }; + return .{ ncmds, bw.end }; } 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 padToIdeal = MachO.padToIdeal; const trace = @import("../../tracy.zig").trace; +const Writer = std.io.Writer; const Allocator = mem.Allocator; const MachO = @import("../MachO.zig"); diff --git a/src/link/MachO/Dwarf.zig b/src/link/MachO/Dwarf.zig index fdc3f33bbc..bc5fda2b9e 100644 --- a/src/link/MachO/Dwarf.zig +++ b/src/link/MachO/Dwarf.zig @@ -81,7 +81,7 @@ pub const InfoReader = struct { .dwarf64 => 12, } + cuh_length; 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 == code) return; @@ -174,14 +174,14 @@ pub const InfoReader = struct { dw.FORM.block1 => try p.readByte(), dw.FORM.block2 => try p.readInt(u16), dw.FORM.block4 => try p.readInt(u32), - dw.FORM.block => try p.readUleb128(u64), + dw.FORM.block => try p.readLeb128(u64), else => unreachable, }; return p.readNBytes(len); } 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); } @@ -191,8 +191,8 @@ pub const InfoReader = struct { dw.FORM.data2, dw.FORM.ref2 => try p.readInt(u16), 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.udata, dw.FORM.ref_udata => try p.readUleb128(u64), - dw.FORM.sdata => @bitCast(try p.readIleb128(i64)), + dw.FORM.udata, dw.FORM.ref_udata => try p.readLeb128(u64), + dw.FORM.sdata => @bitCast(try p.readLeb128(i64)), else => return error.UnhandledConstantForm, }; } @@ -203,7 +203,7 @@ pub const InfoReader = struct { dw.FORM.strx2, dw.FORM.addrx2 => try p.readInt(u16), dw.FORM.strx3, dw.FORM.addrx3 => error.UnhandledForm, 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, }; } @@ -272,20 +272,10 @@ pub const InfoReader = struct { }; } - pub fn readUleb128(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.readUleb128(Type, creader.reader()); - 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 readLeb128(p: *InfoReader, comptime Type: type) !Type { + var r: std.io.Reader = .fixed(p.bytes()[p.pos..]); + defer p.pos += r.seek; + return r.takeLeb128(Type); } pub fn seekTo(p: *InfoReader, off: u64) !void { @@ -307,10 +297,10 @@ pub const AbbrevReader = struct { pub fn readDecl(p: *AbbrevReader) !?AbbrevDecl { const pos = p.pos; - const code = try p.readUleb128(Code); + const code = try p.readLeb128(Code); if (code == 0) return null; - const tag = try p.readUleb128(Tag); + const tag = try p.readLeb128(Tag); const has_children = (try p.readByte()) > 0; return .{ .code = code, @@ -323,8 +313,8 @@ pub const AbbrevReader = struct { pub fn readAttr(p: *AbbrevReader) !?AbbrevAttr { const pos = p.pos; - const at = try p.readUleb128(At); - const form = try p.readUleb128(Form); + const at = try p.readLeb128(At); + const form = try p.readLeb128(Form); return if (at == 0 and form == 0) null else .{ .at = at, .form = form, @@ -339,12 +329,10 @@ pub const AbbrevReader = struct { return p.bytes()[p.pos]; } - pub fn readUleb128(p: *AbbrevReader, 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.readUleb128(Type, creader.reader()); - p.pos += math.cast(usize, creader.bytes_read) orelse return error.Overflow; - return value; + pub fn readLeb128(p: *AbbrevReader, comptime Type: type) !Type { + var r: std.io.Reader = .fixed(p.bytes()[p.pos..]); + defer p.pos += r.seek; + return r.takeLeb128(Type); } pub fn seekTo(p: *AbbrevReader, off: u64) !void { diff --git a/src/link/MachO/Dylib.zig b/src/link/MachO/Dylib.zig index 01dd25fa8c..d950f791ff 100644 --- a/src/link/MachO/Dylib.zig +++ b/src/link/MachO/Dylib.zig @@ -61,7 +61,7 @@ fn parseBinary(self: *Dylib, macho_file: *MachO) !void { const file = macho_file.getFileHandle(self.file_handle); 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; { @@ -140,7 +140,7 @@ fn parseBinary(self: *Dylib, macho_file: *MachO) !void { if (self.platform) |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), }); 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 // I can work out the rest of the changes first // 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.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 { try self.exports.append(allocator, .{ .name = try self.addString(allocator, name), @@ -207,16 +167,16 @@ pub fn addExport(self: *Dylib, allocator: Allocator, name: []const u8, flags: Ex fn parseTrieNode( self: *Dylib, - it: *TrieIterator, + br: *std.io.Reader, allocator: Allocator, arena: Allocator, prefix: []const u8, ) !void { const tracy = trace(@src()); defer tracy.end(); - const size = try it.readUleb128(); + const size = try br.takeLeb128(u64); if (size > 0) { - const flags = try it.readUleb128(); + const flags = try br.takeLeb128(u8); const kind = flags & macho.EXPORT_SYMBOL_FLAGS_KIND_MASK; const out_flags = Export.Flags{ .abs = kind == macho.EXPORT_SYMBOL_FLAGS_KIND_ABSOLUTE, @@ -224,29 +184,28 @@ fn parseTrieNode( .weak = flags & macho.EXPORT_SYMBOL_FLAGS_WEAK_DEFINITION != 0, }; if (flags & macho.EXPORT_SYMBOL_FLAGS_REEXPORT != 0) { - _ = try it.readUleb128(); // dylib ordinal - const name = try it.readString(); + _ = try br.takeLeb128(u64); // dylib ordinal + const name = try br.takeSentinel(0); try self.addExport(allocator, if (name.len > 0) name else prefix, out_flags); } else if (flags & macho.EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER != 0) { - _ = try it.readUleb128(); // stub offset - _ = try it.readUleb128(); // resolver offset + _ = try br.takeLeb128(u64); // stub offset + _ = try br.takeLeb128(u64); // resolver offset try self.addExport(allocator, prefix, out_flags); } else { - _ = try it.readUleb128(); // VM offset + _ = try br.takeLeb128(u64); // VM offset try self.addExport(allocator, prefix, out_flags); } } - const nedges = try it.readByte(); - + const nedges = try br.takeByte(); for (0..nedges) |_| { - const label = try it.readString(); - const off = try it.readUleb128(); + const label = try br.takeSentinel(0); + const off = try br.takeLeb128(usize); const prefix_label = try std.fmt.allocPrint(arena, "{s}{s}", .{ prefix, label }); - const curr = it.pos; - it.pos = math.cast(usize, off) orelse return error.Overflow; - try self.parseTrieNode(it, allocator, arena, prefix_label); - it.pos = curr; + const seek = br.seek; + br.seek = off; + try self.parseTrieNode(br, allocator, arena, prefix_label); + 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); defer arena.deinit(); - var it: TrieIterator = .{ .data = data }; - try self.parseTrieNode(&it, gpa, arena.allocator(), ""); + var r: std.io.Reader = .fixed(data); + try self.parseTrieNode(&r, gpa, arena.allocator(), ""); } 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; - 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); 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( - 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) { +pub fn fmtSymtab(self: *Dylib, macho_file: *MachO) std.fmt.Formatter(Format, Format.symtab) { return .{ .data = .{ .dylib = self, .macho_file = macho_file, } }; } -const FormatContext = struct { +const Format = struct { dylib: *Dylib, macho_file: *MachO, -}; -fn formatSymtab( - ctx: FormatContext, - comptime unused_fmt_string: []const u8, - options: std.fmt.FormatOptions, - 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| { - const ref = dylib.getSymbolRef(@intCast(i), macho_file); - if (ref.getFile(macho_file) == null) { - // TODO any better way of handling this? - try writer.print(" {s} : unclaimed\n", .{sym.getName(macho_file)}); - } else { - try writer.print(" {}\n", .{ref.getSymbol(macho_file).?.fmt(macho_file)}); + fn symtab(f: Format, w: *Writer) Writer.Error!void { + const dylib = f.dylib; + const macho_file = f.macho_file; + try w.writeAll(" globals\n"); + for (dylib.symbols.items, 0..) |sym, i| { + const ref = dylib.getSymbolRef(@intCast(i), macho_file); + if (ref.getFile(macho_file) == null) { + // TODO any better way of handling this? + try w.print(" {s} : unclaimed\n", .{sym.getName(macho_file)}); + } else { + try w.print(" {f}\n", .{ref.getSymbol(macho_file).?.fmt(macho_file)}); + } } } -} +}; pub const TargetMatcher = struct { allocator: Allocator, @@ -948,19 +887,17 @@ const Export = struct { }; }; +const std = @import("std"); const assert = std.debug.assert; -const fat = @import("fat.zig"); const fs = std.fs; const fmt = std.fmt; const log = std.log.scoped(.link); const macho = std.macho; const math = std.math; const mem = std.mem; -const tapi = @import("../tapi.zig"); -const trace = @import("../../tracy.zig").trace; -const std = @import("std"); const Allocator = mem.Allocator; const Path = std.Build.Cache.Path; +const Writer = std.io.Writer; const Dylib = @This(); const File = @import("file.zig").File; @@ -969,3 +906,6 @@ const LoadCommandIterator = macho.LoadCommandIterator; const MachO = @import("../MachO.zig"); const Symbol = @import("Symbol.zig"); const Tbd = tapi.Tbd; +const fat = @import("fat.zig"); +const tapi = @import("../tapi.zig"); +const trace = @import("../../tracy.zig").trace; diff --git a/src/link/MachO/InternalObject.zig b/src/link/MachO/InternalObject.zig index 0218f0c1bb..1329572dc8 100644 --- a/src/link/MachO/InternalObject.zig +++ b/src/link/MachO/InternalObject.zig @@ -261,7 +261,7 @@ fn addObjcMethnameSection(self: *InternalObject, methname: []const u8, macho_fil sect.offset = @intCast(self.objc_methnames.items.len); 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 sym_index = try self.addSymbol(gpa); @@ -836,62 +836,48 @@ fn needsObjcMsgsendSymbol(self: InternalObject) bool { return false; } -const FormatContext = struct { +const Format = struct { self: *InternalObject, macho_file: *MachO, + + fn atoms(f: Format, w: *Writer) Writer.Error!void { + try w.writeAll(" atoms\n"); + for (f.self.getAtoms()) |atom_index| { + const atom = f.self.getAtom(atom_index) orelse continue; + try w.print(" {f}\n", .{atom.fmt(f.macho_file)}); + } + } + + fn symtab(f: Format, w: *Writer) Writer.Error!void { + const macho_file = f.macho_file; + const self = f.self; + try w.writeAll(" symbols\n"); + for (self.symbols.items, 0..) |sym, i| { + const ref = self.getSymbolRef(@intCast(i), macho_file); + if (ref.getFile(macho_file) == null) { + // TODO any better way of handling this? + try w.print(" {s} : unclaimed\n", .{sym.getName(macho_file)}); + } else { + try w.print(" {f}\n", .{ref.getSymbol(macho_file).?.fmt(macho_file)}); + } + } + } }; -pub fn fmtAtoms(self: *InternalObject, macho_file: *MachO) std.fmt.Formatter(formatAtoms) { +pub fn fmtAtoms(self: *InternalObject, macho_file: *MachO) std.fmt.Formatter(Format, Format.atoms) { return .{ .data = .{ .self = self, .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; - 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) { +pub fn fmtSymtab(self: *InternalObject, macho_file: *MachO) std.fmt.Formatter(Format, Format.symtab) { return .{ .data = .{ .self = self, .macho_file = macho_file, } }; } -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| { - const ref = self.getSymbolRef(@intCast(i), macho_file); - if (ref.getFile(macho_file) == null) { - // TODO any better way of handling this? - try writer.print(" {s} : unclaimed\n", .{sym.getName(macho_file)}); - } else { - try writer.print(" {}\n", .{ref.getSymbol(macho_file).?.fmt(macho_file)}); - } - } -} - const Section = struct { header: macho.section_64, relocs: std.ArrayListUnmanaged(Relocation) = .empty, @@ -908,6 +894,7 @@ const macho = std.macho; const mem = std.mem; const std = @import("std"); const trace = @import("../../tracy.zig").trace; +const Writer = std.io.Writer; const Allocator = std.mem.Allocator; const Atom = @import("Atom.zig"); diff --git a/src/link/MachO/Object.zig b/src/link/MachO/Object.zig index 2086c261bf..2169c7e7d6 100644 --- a/src/link/MachO/Object.zig +++ b/src/link/MachO/Object.zig @@ -72,7 +72,7 @@ pub fn parse(self: *Object, macho_file: *MachO) !void { const tracy = trace(@src()); defer tracy.end(); - log.debug("parsing {}", .{self.fmtPath()}); + log.debug("parsing {f}", .{self.fmtPath()}); const gpa = macho_file.base.comp.gpa; 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 (!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), }); 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 // I can work out the rest of the changes first // 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.version, // platform.version, @@ -308,7 +308,9 @@ fn initSubsections(self: *Object, allocator: Allocator, nlists: anytype) !void { } else nlists.len; 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); 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, .{ @@ -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 // would exceed section boundaries). In order to facilitate this behaviour, // 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); const atom_index = try self.addAtom(allocator, .{ .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| { switch (rec.tag) { .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 const size = try macho_file.cast(usize, self.output_ar_state.size); 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 const file = macho_file.getFileHandle(self.file_handle); // 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); const amt = try file.preadAll(data, self.offset); if (amt != size) return error.InputOutput; - try writer.writeAll(data); + try bw.writeAll(data); } 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); } - @memset(sections_data, &[0]u8{}); + @memset(sections_data, &.{}); const file = macho_file.getFileHandle(self.file_handle); for (headers, 0..) |header, n_sect| { @@ -2512,165 +2516,114 @@ pub fn readSectionData(self: Object, allocator: Allocator, file: File.Handle, n_ return data; } -pub fn format( - 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 { +const Format = struct { object: *Object, macho_file: *MachO, + + fn atoms(f: Format, w: *Writer) Writer.Error!void { + const object = f.object; + const macho_file = f.macho_file; + try w.writeAll(" atoms\n"); + for (object.getAtoms()) |atom_index| { + const atom = object.getAtom(atom_index) orelse continue; + try w.print(" {f}\n", .{atom.fmt(macho_file)}); + } + } + fn cies(f: Format, w: *Writer) Writer.Error!void { + const object = f.object; + try w.writeAll(" cies\n"); + for (object.cies.items, 0..) |cie, i| { + try w.print(" cie({d}) : {f}\n", .{ i, cie.fmt(f.macho_file) }); + } + } + fn fdes(f: Format, w: *Writer) Writer.Error!void { + const object = f.object; + try w.writeAll(" fdes\n"); + for (object.fdes.items, 0..) |fde, i| { + try w.print(" fde({d}) : {f}\n", .{ i, fde.fmt(f.macho_file) }); + } + } + fn unwindRecords(f: Format, w: *Writer) Writer.Error!void { + const object = f.object; + const macho_file = f.macho_file; + try w.writeAll(" unwind records\n"); + for (object.unwind_records_indexes.items) |rec| { + try w.print(" rec({d}) : {f}\n", .{ rec, object.getUnwindRecord(rec).fmt(macho_file) }); + } + } + + fn formatSymtab(f: Format, w: *Writer) Writer.Error!void { + const object = f.object; + const macho_file = f.macho_file; + try w.writeAll(" symbols\n"); + for (object.symbols.items, 0..) |sym, i| { + const ref = object.getSymbolRef(@intCast(i), macho_file); + if (ref.getFile(macho_file) == null) { + // TODO any better way of handling this? + try w.print(" {s} : unclaimed\n", .{sym.getName(macho_file)}); + } else { + try w.print(" {f}\n", .{ref.getSymbol(macho_file).?.fmt(macho_file)}); + } + } + for (object.stab_files.items) |sf| { + try w.print(" stabs({s},{s},{s})\n", .{ + sf.getCompDir(object.*), + sf.getTuName(object.*), + sf.getOsoPath(object.*), + }); + for (sf.stabs.items) |stab| { + try w.print(" {f}", .{stab.fmt(object.*)}); + } + } + } }; -pub fn fmtAtoms(self: *Object, macho_file: *MachO) std.fmt.Formatter(formatAtoms) { +pub fn fmtAtoms(self: *Object, macho_file: *MachO) std.fmt.Formatter(Format, Format.atoms) { return .{ .data = .{ .object = self, .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 object = ctx.object; - const macho_file = ctx.macho_file; - try writer.writeAll(" atoms\n"); - for (object.getAtoms()) |atom_index| { - const atom = object.getAtom(atom_index) orelse continue; - try writer.print(" {}\n", .{atom.fmt(macho_file)}); - } -} - -pub fn fmtCies(self: *Object, macho_file: *MachO) std.fmt.Formatter(formatCies) { +pub fn fmtCies(self: *Object, macho_file: *MachO) std.fmt.Formatter(Format, Format.cies) { return .{ .data = .{ .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| { - try writer.print(" cie({d}) : {}\n", .{ i, cie.fmt(ctx.macho_file) }); - } -} - -pub fn fmtFdes(self: *Object, macho_file: *MachO) std.fmt.Formatter(formatFdes) { +pub fn fmtFdes(self: *Object, macho_file: *MachO) std.fmt.Formatter(Format, Format.fdes) { return .{ .data = .{ .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| { - try writer.print(" fde({d}) : {}\n", .{ i, fde.fmt(ctx.macho_file) }); - } -} - -pub fn fmtUnwindRecords(self: *Object, macho_file: *MachO) std.fmt.Formatter(formatUnwindRecords) { +pub fn fmtUnwindRecords(self: *Object, macho_file: *MachO) std.fmt.Formatter(Format, Format.unwindRecords) { return .{ .data = .{ .object = self, .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| { - try writer.print(" rec({d}) : {}\n", .{ rec, object.getUnwindRecord(rec).fmt(macho_file) }); - } -} - -pub fn fmtSymtab(self: *Object, macho_file: *MachO) std.fmt.Formatter(formatSymtab) { +pub fn fmtSymtab(self: *Object, macho_file: *MachO) std.fmt.Formatter(Format, Format.symtab) { return .{ .data = .{ .object = self, .macho_file = macho_file, } }; } -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| { - const ref = object.getSymbolRef(@intCast(i), macho_file); - if (ref.getFile(macho_file) == null) { - // TODO any better way of handling this? - try writer.print(" {s} : unclaimed\n", .{sym.getName(macho_file)}); - } else { - try writer.print(" {}\n", .{ref.getSymbol(macho_file).?.fmt(macho_file)}); - } - } - for (object.stab_files.items) |sf| { - try writer.print(" stabs({s},{s},{s})\n", .{ - sf.getCompDir(object.*), - sf.getTuName(object.*), - sf.getOsoPath(object.*), - }); - for (sf.stabs.items) |stab| { - try writer.print(" {}", .{stab.fmt(object.*)}); - } - } -} - pub fn fmtPath(self: Object) std.fmt.Formatter(Object, formatPath) { 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| { - try writer.print("{f}({s})", .{ - @as(Path, ar.path), object.path.basename(), + try w.print("{f}({s})", .{ + ar.path, object.path.basename(), }); } else { - try writer.print("{f}", .{@as(Path, object.path)}); + try w.print("{f}", .{object.path}); } } @@ -2724,43 +2677,26 @@ const StabFile = struct { return object.symbols.items[index]; } - pub fn format( + const Format = struct { stab: Stab, - comptime unused_fmt_string: []const u8, - options: std.fmt.FormatOptions, - writer: anytype, - ) !void { - _ = stab; - _ = unused_fmt_string; - _ = options; - _ = writer; - @compileError("do not format stabs directly"); - } + object: Object, - const StabFormatContext = struct { Stab, Object }; + fn default(f: Stab.Format, w: *Writer) Writer.Error!void { + const stab = f.stab; + const sym = stab.getSymbol(f.object).?; + if (stab.is_func) { + try w.print("func({d})", .{stab.index.?}); + } else if (sym.visibility == .global) { + try w.print("gsym({d})", .{stab.index.?}); + } else { + try w.print("stsym({d})", .{stab.index.?}); + } + } + }; - pub fn fmt(stab: Stab, object: Object) std.fmt.Formatter(format2) { + pub fn fmt(stab: Stab, object: Object) std.fmt.Formatter(Stab.Format, Stab.Format.default) { 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) { - try writer.print("func({d})", .{stab.index.?}); - } else if (sym.visibility == .global) { - try writer.print("gsym({d})", .{stab.index.?}); - } else { - try writer.print("stsym({d})", .{stab.index.?}); - } - } }; }; @@ -3150,17 +3086,18 @@ const aarch64 = struct { } }; +const std = @import("std"); const assert = std.debug.assert; -const eh_frame = @import("eh_frame.zig"); const log = std.log.scoped(.link); const macho = std.macho; const math = std.math; const mem = std.mem; -const trace = @import("../../tracy.zig").trace; -const std = @import("std"); 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 Atom = @import("Atom.zig"); const Cie = eh_frame.Cie; diff --git a/src/link/MachO/Relocation.zig b/src/link/MachO/Relocation.zig index c732dc3a89..77f29d2e58 100644 --- a/src/link/MachO/Relocation.zig +++ b/src/link/MachO/Relocation.zig @@ -70,57 +70,51 @@ pub fn lessThan(ctx: void, lhs: Relocation, rhs: Relocation) bool { 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(formatPretty) { +pub fn fmtPretty(rel: Relocation, cpu_arch: std.Target.Cpu.Arch) std.fmt.Formatter(Format, Format.pretty) { return .{ .data = .{ rel, cpu_arch } }; } -fn formatPretty( - ctx: FormatCtx, - comptime unused_fmt_string: []const u8, - options: std.fmt.FormatOptions, - writer: anytype, -) !void { - _ = options; - _ = unused_fmt_string; - const rel, const cpu_arch = ctx; - const str = switch (rel.type) { - .signed => "X86_64_RELOC_SIGNED", - .signed1 => "X86_64_RELOC_SIGNED_1", - .signed2 => "X86_64_RELOC_SIGNED_2", - .signed4 => "X86_64_RELOC_SIGNED_4", - .got_load => "X86_64_RELOC_GOT_LOAD", - .tlv => "X86_64_RELOC_TLV", - .page => "ARM64_RELOC_PAGE21", - .pageoff => "ARM64_RELOC_PAGEOFF12", - .got_load_page => "ARM64_RELOC_GOT_LOAD_PAGE21", - .got_load_pageoff => "ARM64_RELOC_GOT_LOAD_PAGEOFF12", - .tlvp_page => "ARM64_RELOC_TLVP_LOAD_PAGE21", - .tlvp_pageoff => "ARM64_RELOC_TLVP_LOAD_PAGEOFF12", - .branch => switch (cpu_arch) { - .x86_64 => "X86_64_RELOC_BRANCH", - .aarch64 => "ARM64_RELOC_BRANCH26", - else => unreachable, - }, - .got => switch (cpu_arch) { - .x86_64 => "X86_64_RELOC_GOT", - .aarch64 => "ARM64_RELOC_POINTER_TO_GOT", - else => unreachable, - }, - .subtractor => switch (cpu_arch) { - .x86_64 => "X86_64_RELOC_SUBTRACTOR", - .aarch64 => "ARM64_RELOC_SUBTRACTOR", - else => unreachable, - }, - .unsigned => switch (cpu_arch) { - .x86_64 => "X86_64_RELOC_UNSIGNED", - .aarch64 => "ARM64_RELOC_UNSIGNED", - else => unreachable, - }, - }; - try writer.writeAll(str); -} +const Format = struct { + relocation: Relocation, + arch: std.Target.Cpu.Arch, + + fn pretty(f: Format, w: *Writer) Writer.Error!void { + try w.writeAll(switch (f.relocation.type) { + .signed => "X86_64_RELOC_SIGNED", + .signed1 => "X86_64_RELOC_SIGNED_1", + .signed2 => "X86_64_RELOC_SIGNED_2", + .signed4 => "X86_64_RELOC_SIGNED_4", + .got_load => "X86_64_RELOC_GOT_LOAD", + .tlv => "X86_64_RELOC_TLV", + .page => "ARM64_RELOC_PAGE21", + .pageoff => "ARM64_RELOC_PAGEOFF12", + .got_load_page => "ARM64_RELOC_GOT_LOAD_PAGE21", + .got_load_pageoff => "ARM64_RELOC_GOT_LOAD_PAGEOFF12", + .tlvp_page => "ARM64_RELOC_TLVP_LOAD_PAGE21", + .tlvp_pageoff => "ARM64_RELOC_TLVP_LOAD_PAGEOFF12", + .branch => switch (f.arch) { + .x86_64 => "X86_64_RELOC_BRANCH", + .aarch64 => "ARM64_RELOC_BRANCH26", + else => unreachable, + }, + .got => switch (f.arch) { + .x86_64 => "X86_64_RELOC_GOT", + .aarch64 => "ARM64_RELOC_POINTER_TO_GOT", + else => unreachable, + }, + .subtractor => switch (f.arch) { + .x86_64 => "X86_64_RELOC_SUBTRACTOR", + .aarch64 => "ARM64_RELOC_SUBTRACTOR", + else => unreachable, + }, + .unsigned => switch (f.arch) { + .x86_64 => "X86_64_RELOC_UNSIGNED", + .aarch64 => "ARM64_RELOC_UNSIGNED", + else => unreachable, + }, + }); + } +}; pub const Type = enum { // x86_64 @@ -164,10 +158,11 @@ pub const Type = enum { const Tag = enum { local, @"extern" }; +const std = @import("std"); const assert = std.debug.assert; const macho = std.macho; const math = std.math; -const std = @import("std"); +const Writer = std.io.Writer; const Atom = @import("Atom.zig"); const MachO = @import("../MachO.zig"); diff --git a/src/link/MachO/Symbol.zig b/src/link/MachO/Symbol.zig index be126b0963..b205740055 100644 --- a/src/link/MachO/Symbol.zig +++ b/src/link/MachO/Symbol.zig @@ -286,71 +286,51 @@ pub fn setOutputSym(symbol: Symbol, macho_file: *MachO, out: *macho.nlist_64) vo } } -pub fn format( - 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) { +pub fn fmt(symbol: Symbol, macho_file: *MachO) std.fmt.Formatter(Format, Format.default) { return .{ .data = .{ .symbol = symbol, .macho_file = macho_file, } }; } -fn format2( - ctx: FormatContext, - comptime unused_fmt_string: []const u8, - options: std.fmt.FormatOptions, - writer: anytype, -) !void { - _ = options; - _ = unused_fmt_string; - const symbol = ctx.symbol; - try writer.print("%{d} : {s} : @{x}", .{ - symbol.nlist_idx, - symbol.getName(ctx.macho_file), - symbol.getAddress(.{}, ctx.macho_file), - }); - if (symbol.getFile(ctx.macho_file)) |file| { - if (symbol.getOutputSectionIndex(ctx.macho_file) != 0) { - try writer.print(" : sect({d})", .{symbol.getOutputSectionIndex(ctx.macho_file)}); - } - if (symbol.getAtom(ctx.macho_file)) |atom| { - try writer.print(" : atom({d})", .{atom.atom_index}); - } - var buf: [3]u8 = .{'_'} ** 3; - if (symbol.flags.@"export") buf[0] = 'E'; - if (symbol.flags.import) buf[1] = 'I'; - switch (symbol.visibility) { - .local => buf[2] = 'L', - .hidden => buf[2] = 'H', - .global => buf[2] = 'G', - } - try writer.print(" : {s}", .{&buf}); - if (symbol.flags.weak) try writer.writeAll(" : weak"); - if (symbol.isSymbolStab(ctx.macho_file)) try writer.writeAll(" : stab"); - switch (file) { - .zig_object => |x| try writer.print(" : zig_object({d})", .{x.index}), - .internal => |x| try writer.print(" : internal({d})", .{x.index}), - .object => |x| try writer.print(" : object({d})", .{x.index}), - .dylib => |x| try writer.print(" : dylib({d})", .{x.index}), - } - } else try writer.writeAll(" : unresolved"); -} +const Format = struct { + symbol: Symbol, + macho_file: *MachO, + + fn format2(f: Format, w: *Writer) Writer.Error!void { + const symbol = f.symbol; + try w.print("%{d} : {s} : @{x}", .{ + symbol.nlist_idx, + symbol.getName(f.macho_file), + symbol.getAddress(.{}, f.macho_file), + }); + if (symbol.getFile(f.macho_file)) |file| { + if (symbol.getOutputSectionIndex(f.macho_file) != 0) { + try w.print(" : sect({d})", .{symbol.getOutputSectionIndex(f.macho_file)}); + } + if (symbol.getAtom(f.macho_file)) |atom| { + try w.print(" : atom({d})", .{atom.atom_index}); + } + var buf: [3]u8 = .{'_'} ** 3; + if (symbol.flags.@"export") buf[0] = 'E'; + if (symbol.flags.import) buf[1] = 'I'; + switch (symbol.visibility) { + .local => buf[2] = 'L', + .hidden => buf[2] = 'H', + .global => buf[2] = 'G', + } + try w.print(" : {s}", .{&buf}); + if (symbol.flags.weak) try w.writeAll(" : weak"); + if (symbol.isSymbolStab(f.macho_file)) try w.writeAll(" : stab"); + switch (file) { + .zig_object => |x| try w.print(" : zig_object({d})", .{x.index}), + .internal => |x| try w.print(" : internal({d})", .{x.index}), + .object => |x| try w.print(" : object({d})", .{x.index}), + .dylib => |x| try w.print(" : dylib({d})", .{x.index}), + } + } else try w.writeAll(" : unresolved"); + } +}; pub const Flags = packed struct { /// Whether the symbol is imported at runtime. @@ -437,6 +417,7 @@ pub const Index = u32; const assert = std.debug.assert; const macho = std.macho; const std = @import("std"); +const Writer = std.io.Writer; const Atom = @import("Atom.zig"); const File = @import("file.zig").File; diff --git a/src/link/MachO/Thunk.zig b/src/link/MachO/Thunk.zig index d720d4fd25..5821c88ab7 100644 --- a/src/link/MachO/Thunk.zig +++ b/src/link/MachO/Thunk.zig @@ -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; } -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| { const sym = ref.getSymbol(macho_file).?; const saddr = thunk.getAddress(macho_file) + i * trampoline_size; const taddr = sym.getAddress(.{}, macho_file); 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); - try writer.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.add(.x16, .x16, off, false).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( - 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) { +pub fn fmt(thunk: Thunk, macho_file: *MachO) std.fmt.Formatter(Format, Format.default) { return .{ .data = .{ .thunk = thunk, .macho_file = macho_file, } }; } -const FormatContext = struct { +const Format = struct { thunk: Thunk, macho_file: *MachO, -}; -fn format2( - ctx: FormatContext, - comptime unused_fmt_string: []const u8, - options: std.fmt.FormatOptions, - 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| { - const sym = ref.getSymbol(macho_file).?; - try writer.print(" {} : {s} : @{x}\n", .{ ref, sym.getName(macho_file), sym.value }); + fn default(f: Format, w: *Writer) Writer.Error!void { + const thunk = f.thunk; + const macho_file = f.macho_file; + try w.print("@{x} : size({x})\n", .{ thunk.value, thunk.size() }); + for (thunk.symbols.keys()) |ref| { + const sym = ref.getSymbol(macho_file).?; + try w.print(" {f} : {s} : @{x}\n", .{ ref, sym.getName(macho_file), sym.value }); + } } -} +}; const trampoline_size = 3 * @sizeOf(u32); @@ -115,6 +95,7 @@ const math = std.math; const mem = std.mem; const std = @import("std"); const trace = @import("../../tracy.zig").trace; +const Writer = std.io.Writer; const Allocator = mem.Allocator; const Atom = @import("Atom.zig"); diff --git a/src/link/MachO/UnwindInfo.zig b/src/link/MachO/UnwindInfo.zig index ffeeaddb23..17676fd826 100644 --- a/src/link/MachO/UnwindInfo.zig +++ b/src/link/MachO/UnwindInfo.zig @@ -133,7 +133,7 @@ pub fn generate(info: *UnwindInfo, macho_file: *MachO) !void { for (info.records.items) |ref| { const rec = ref.getUnwindRecord(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.length, atom.getName(macho_file), @@ -202,7 +202,7 @@ pub fn generate(info: *UnwindInfo, macho_file: *MachO) !void { if (i >= max_common_encodings) break; if (slice[i].count < 2) continue; 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; } - log.debug("{}", .{page.fmt(info.*)}); + log.debug("{f}", .{page.fmt(info.*)}); try info.pages.append(gpa, page); } @@ -289,13 +289,10 @@ pub fn calcSize(info: UnwindInfo) usize { 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 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_count: u32 = info.common_encodings_count; 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_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, .commonEncodingsArrayCount = common_encodings_count, .personalityArraySectionOffset = personalities_offset, @@ -312,11 +309,11 @@ pub fn write(info: UnwindInfo, macho_file: *MachO, buffer: []u8) !void { .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| { 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))); @@ -325,7 +322,7 @@ pub fn write(info: UnwindInfo, macho_file: *MachO, buffer: []u8) !void { for (info.pages.items, 0..) |page, i| { assert(page.count > 0); 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)), .secondLevelPagesSectionOffset = @as(u32, @intCast(pages_base_offset + i * second_level_page_bytes)), .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 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, .secondLevelPagesSectionOffset = 0, .lsdaIndexArraySectionOffset = lsda_base_offset + @@ -344,23 +341,20 @@ pub fn write(info: UnwindInfo, macho_file: *MachO, buffer: []u8) !void { for (info.lsdas.items) |index| { 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)), .lsdaOffset = @as(u32, @intCast(rec.getLsdaAddress(macho_file) - seg.vmaddr)), }); } for (info.pages.items) |page| { - const start = stream.pos; - try page.write(info, macho_file, writer); - const nwritten = stream.pos - start; - if (nwritten < second_level_page_bytes) { - const padding = math.cast(usize, second_level_page_bytes - nwritten) orelse return error.Overflow; - try writer.writeByteNTimes(0, padding); - } + const start = bw.count; + try page.write(info, macho_file, bw); + const nwritten = bw.count - start; + try bw.splatByteAll(0, math.cast(usize, second_level_page_bytes - nwritten) orelse return error.Overflow); } - @memset(buffer[stream.pos..], 0); + @memset(bw.unusedCapacitySlice(), 0); } fn getOrPutPersonalityFunction(info: *UnwindInfo, ref: MachO.Ref) error{TooManyPersonalities}!u2 { @@ -455,15 +449,9 @@ pub const Encoding = extern struct { return enc.enc == other.enc; } - pub fn format( - enc: Encoding, - comptime unused_fmt_string: []const u8, - options: std.fmt.FormatOptions, - writer: anytype, - ) !void { - _ = unused_fmt_string; - _ = options; - try writer.print("0x{x:0>8}", .{enc.enc}); + pub fn format(enc: Encoding, w: *Writer, comptime unused_fmt_string: []const u8) Writer.Error!void { + comptime assert(unused_fmt_string.len == 0); + try w.print("0x{x:0>8}", .{enc.enc}); } }; @@ -517,48 +505,28 @@ pub const Record = struct { return lsda.getAddress(macho_file) + rec.lsda_offset; } - pub fn format( - 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) { + pub fn fmt(rec: Record, macho_file: *MachO) std.fmt.Formatter(Format, Format.default) { return .{ .data = .{ .rec = rec, .macho_file = macho_file, } }; } - const FormatContext = struct { + const Format = struct { rec: Record, macho_file: *MachO, - }; - fn format2( - ctx: FormatContext, - comptime unused_fmt_string: []const u8, - options: std.fmt.FormatOptions, - 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, - }); - if (rec.enc.isDwarf(macho_file)) try writer.print(" : fde({d})", .{rec.fde}); - try writer.print(" : {s}", .{rec.getAtom(macho_file).getName(macho_file)}); - if (!rec.alive) try writer.writeAll(" : [*]"); - } + fn default(f: Format, w: *Writer) Writer.Error!void { + const rec = f.rec; + const macho_file = f.macho_file; + try w.print("{x} : len({x})", .{ + rec.enc.enc, rec.length, + }); + if (rec.enc.isDwarf(macho_file)) try w.print(" : fde({d})", .{rec.fde}); + try w.print(" : {s}", .{rec.getAtom(macho_file).getName(macho_file)}); + if (!rec.alive) try w.writeAll(" : [*]"); + } + }; pub const Index = u32; @@ -613,45 +581,25 @@ const Page = struct { return null; } - fn format( - 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 { + const Format = struct { page: Page, info: UnwindInfo, - }; - fn format2( - 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 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 fmt(page: Page, info: UnwindInfo) std.fmt.Formatter(format2) { + fn fmt(page: Page, info: UnwindInfo) std.fmt.Formatter(Format, Format.default) { return .{ .data = .{ .page = page, .info = info, @@ -720,6 +668,7 @@ const macho = std.macho; const math = std.math; const mem = std.mem; const trace = @import("../../tracy.zig").trace; +const Writer = std.io.Writer; const Allocator = mem.Allocator; const Atom = @import("Atom.zig"); diff --git a/src/link/MachO/ZigObject.zig b/src/link/MachO/ZigObject.zig index df0be97ba6..023fad64c7 100644 --- a/src/link/MachO/ZigObject.zig +++ b/src/link/MachO/ZigObject.zig @@ -317,12 +317,12 @@ pub fn updateArSize(self: *ZigObject) void { 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 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 - try writer.writeAll(self.data.items); + try bw.writeAll(self.data.items); } pub fn claimUnresolved(self: *ZigObject, macho_file: *MachO) void { @@ -618,7 +618,7 @@ pub fn getNavVAddr( const zcu = pt.zcu; const ip = &zcu.intern_pool; 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( macho_file, nav.name.toSlice(ip), @@ -884,7 +884,6 @@ pub fn updateNav( defer debug_wip_nav.deinit(); dwarf.finishWipNav(pt, nav_index, &debug_wip_nav) catch |err| switch (err) { 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)}), }; } @@ -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) { 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 if (self.dwarf) |*dwarf| try dwarf.updateComptimeNav(pt, nav_index); @@ -943,7 +941,7 @@ fn updateNavCode( const ip = &zcu.intern_pool; 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 required_alignment = switch (pt.navAlignment(nav_index)) { @@ -981,7 +979,7 @@ fn updateNavCode( if (need_realloc) { atom.grow(macho_file) catch |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) { sym.value = 0; nlist.n_value = 0; @@ -1023,7 +1021,7 @@ fn updateTlv( const ip = &pt.zcu.intern_pool; 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 const init_sym_index = try self.createTlvInitializer( @@ -1351,7 +1349,7 @@ fn updateLazySymbol( defer code_buffer.deinit(gpa); 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), Type.fromInterned(lazy_sym.ty).fmt(pt), }); @@ -1430,7 +1428,7 @@ pub fn deleteExport( } 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.*]; self.symtab.items(.size)[nlist_index.*] = 0; @@ -1678,64 +1676,50 @@ pub fn asFile(self: *ZigObject) File { 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 = .{ .self = self, .macho_file = macho_file, } }; } -const FormatContext = struct { +const Format = struct { self: *ZigObject, macho_file: *MachO, -}; -fn formatSymtab( - ctx: FormatContext, - comptime unused_fmt_string: []const u8, - options: std.fmt.FormatOptions, - 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| { - const ref = self.getSymbolRef(@intCast(i), macho_file); - if (ref.getFile(macho_file) == null) { - // TODO any better way of handling this? - try writer.print(" {s} : unclaimed\n", .{sym.getName(macho_file)}); - } else { - try writer.print(" {}\n", .{ref.getSymbol(macho_file).?.fmt(macho_file)}); + fn symtab(f: Format, w: *Writer) Writer.Error!void { + try w.writeAll(" symbols\n"); + const self = f.self; + const macho_file = f.macho_file; + for (self.symbols.items, 0..) |sym, i| { + const ref = self.getSymbolRef(@intCast(i), macho_file); + if (ref.getFile(macho_file) == null) { + // TODO any better way of handling this? + try w.print(" {s} : unclaimed\n", .{sym.getName(macho_file)}); + } else { + 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 = .{ .self = self, .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 { symbol_index: Symbol.Index, /// A list of all exports aliases of this Av. @@ -1797,6 +1781,7 @@ const mem = std.mem; const target_util = @import("../../target.zig"); const trace = @import("../../tracy.zig").trace; const std = @import("std"); +const Writer = std.io.Writer; const Allocator = std.mem.Allocator; const Archive = @import("Archive.zig"); diff --git a/src/link/MachO/dead_strip.zig b/src/link/MachO/dead_strip.zig index 24d7e18d1a..17bd302d82 100644 --- a/src/link/MachO/dead_strip.zig +++ b/src/link/MachO/dead_strip.zig @@ -117,7 +117,7 @@ fn mark(roots: []*Atom, objects: []const File.Index, macho_file: *MachO) void { fn markLive(atom: *Atom, macho_file: *MachO) void { assert(atom.visited.load(.seq_cst)); 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, atom.atom_index, atom.getName(macho_file), @@ -196,15 +196,9 @@ const Level = struct { self.value += 1; } - pub fn format( - self: *const @This(), - comptime unused_fmt_string: []const u8, - options: std.fmt.FormatOptions, - writer: anytype, - ) !void { + pub fn format(self: *const @This(), bw: *Writer, comptime unused_fmt_string: []const u8) Writer.Error!void { _ = unused_fmt_string; - _ = options; - try writer.writeByteNTimes(' ', self.value); + try bw.splatByteAll(' ', self.value); } }; @@ -219,6 +213,7 @@ const mem = std.mem; const trace = @import("../../tracy.zig").trace; const track_live_log = std.log.scoped(.dead_strip_track_live); const std = @import("std"); +const Writer = std.io.Writer; const Allocator = mem.Allocator; const Atom = @import("Atom.zig"); diff --git a/src/link/MachO/dyld_info/Rebase.zig b/src/link/MachO/dyld_info/Rebase.zig index dc4fcbcc1d..5fdba220a7 100644 --- a/src/link/MachO/dyld_info/Rebase.zig +++ b/src/link/MachO/dyld_info/Rebase.zig @@ -3,7 +3,7 @@ buffer: std.ArrayListUnmanaged(u8) = .empty, pub const Entry = struct { offset: u64, - segment_id: u8, + segment_id: u4, pub fn lessThan(ctx: void, entry: Entry, other: Entry) bool { _ = ctx; @@ -110,33 +110,35 @@ pub fn updateSize(rebase: *Rebase, macho_file: *MachO) !void { fn finalize(rebase: *Rebase, gpa: Allocator) !void { 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", .{}); std.mem.sort(Entry, rebase.entries.items, {}, Entry.lessThan); - try setTypePointer(writer); + try setTypePointer(bw); var start: usize = 0; var seg_id: ?u8 = null; for (rebase.entries.items, 0..) |entry, i| { 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; start = i; } - try finalizeSegment(rebase.entries.items[start..], writer); - try done(writer); + try finalizeSegment(rebase.entries.items[start..], bw); + 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; const segment_id = entries[0].segment_id; var offset = entries[0].offset; - try setSegmentOffset(segment_id, offset, writer); + try setSegmentOffset(segment_id, offset, bw); var count: usize = 0; var skip: u64 = 0; @@ -155,7 +157,7 @@ fn finalizeSegment(entries: []const Entry, writer: anytype) !void { .start => { if (offset < current_offset) { const delta = current_offset - offset; - try addAddr(delta, writer); + try addAddr(delta, bw); offset += delta; } state = .times; @@ -175,7 +177,7 @@ fn finalizeSegment(entries: []const Entry, writer: anytype) !void { offset += skip; i -= 1; } else { - try rebaseTimes(count, writer); + try rebaseTimes(count, bw); state = .start; i -= 1; } @@ -184,9 +186,9 @@ fn finalizeSegment(entries: []const Entry, writer: anytype) !void { if (current_offset < offset) { count -= 1; if (count == 1) { - try rebaseAddAddr(skip, writer); + try rebaseAddAddr(skip, bw); } else { - try rebaseTimesSkip(count, skip, writer); + try rebaseTimesSkip(count, skip, bw); } state = .start; offset = offset - (@sizeOf(u64) + skip); @@ -199,7 +201,7 @@ fn finalizeSegment(entries: []const Entry, writer: anytype) !void { count += 1; offset += @sizeOf(u64) + skip; } else { - try rebaseTimesSkip(count, skip, writer); + try rebaseTimesSkip(count, skip, bw); state = .start; i -= 1; } @@ -210,68 +212,66 @@ fn finalizeSegment(entries: []const Entry, writer: anytype) !void { switch (state) { .start => unreachable, .times => { - try rebaseTimes(count, writer); + try rebaseTimes(count, bw); }, .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}); - 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 }); - try writer.writeByte(macho.REBASE_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB | @as(u4, @truncate(segment_id))); - try std.leb.writeUleb128(writer, offset); + try bw.writeByte(macho.REBASE_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB | @as(u4, @truncate(segment_id))); + 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}); - try writer.writeByte(macho.REBASE_OPCODE_DO_REBASE_ADD_ADDR_ULEB); - try std.leb.writeUleb128(writer, addr); + try bw.writeByte(macho.REBASE_OPCODE_DO_REBASE_ADD_ADDR_ULEB); + 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}); 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 { - try writer.writeByte(macho.REBASE_OPCODE_DO_REBASE_ULEB_TIMES); - try std.leb.writeUleb128(writer, count); + try bw.writeByte(macho.REBASE_OPCODE_DO_REBASE_ULEB_TIMES); + 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 }); - try writer.writeByte(macho.REBASE_OPCODE_DO_REBASE_ULEB_TIMES_SKIPPING_ULEB); - try std.leb.writeUleb128(writer, count); - try std.leb.writeUleb128(writer, skip); + try bw.writeByte(macho.REBASE_OPCODE_DO_REBASE_ULEB_TIMES_SKIPPING_ULEB); + try bw.writeLeb128(count); + 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}); - if (std.mem.isAlignedGeneric(u64, addr, @sizeOf(u64))) { - const imm = @divExact(addr, @sizeOf(u64)); - if (imm <= 0xf) { - try writer.writeByte(macho.REBASE_OPCODE_ADD_ADDR_IMM_SCALED | @as(u4, @truncate(imm))); - return; - } - } - try writer.writeByte(macho.REBASE_OPCODE_ADD_ADDR_ULEB); - try std.leb.writeUleb128(writer, addr); + if (std.math.divExact(u64, addr, @sizeOf(u64))) |scaled| { + if (std.math.cast(u4, scaled)) |imm_scaled| return bw.writeByte( + macho.REBASE_OPCODE_ADD_ADDR_IMM_SCALED | imm_scaled, + ); + } else |_| {} + try bw.writeByte(macho.REBASE_OPCODE_ADD_ADDR_ULEB); + try bw.writeLeb128(addr); } -fn done(writer: anytype) !void { +fn done(bw: *Writer) Writer.Error!void { 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 { - try writer.writeAll(rebase.buffer.items); +pub fn write(rebase: Rebase, bw: *Writer) Writer.Error!void { + try bw.writeAll(rebase.buffer.items); } test "rebase - no entries" { @@ -654,9 +654,10 @@ const log = std.log.scoped(.link_dyld_info); const macho = std.macho; const mem = std.mem; const testing = std.testing; -const trace = @import("../../../tracy.zig").trace; - const Allocator = mem.Allocator; +const Writer = std.io.Writer; + +const trace = @import("../../../tracy.zig").trace; const File = @import("../file.zig").File; const MachO = @import("../../MachO.zig"); const Rebase = @This(); diff --git a/src/link/MachO/dyld_info/Trie.zig b/src/link/MachO/dyld_info/Trie.zig index 8224dc8424..841bb9541b 100644 --- a/src/link/MachO/dyld_info/Trie.zig +++ b/src/link/MachO/dyld_info/Trie.zig @@ -31,7 +31,7 @@ /// The root node of the trie. root: ?Node.Index = null, -buffer: std.ArrayListUnmanaged(u8) = .empty, +buffer: []u8 = &.{}, nodes: std.MultiArrayList(Node) = .{}, edges: std.ArrayListUnmanaged(Edge) = .empty, @@ -123,7 +123,7 @@ pub fn updateSize(self: *Trie, macho_file: *MachO) !void { 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. @@ -138,7 +138,7 @@ fn finalize(self: *Trie, allocator: Allocator) !void { defer ordered_nodes.deinit(); 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(); 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| { - 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. fn finalizeNode(self: *Trie, node_index: Node.Index, offset_in_trie: u32) !FinalizeNodeResult { - var stream = std.io.countingWriter(std.io.null_writer); - const writer = stream.writer(); + var buf: [1024]u8 = undefined; + var bw: Writer = .discarding(&buf); const slice = self.nodes.slice(); var node_size: u32 = 0; if (slice.items(.is_terminal)[node_index]) { const export_flags = slice.items(.export_flags)[node_index]; const vmaddr_offset = slice.items(.vmaddr_offset)[node_index]; - try leb.writeULEB128(writer, export_flags); - try leb.writeULEB128(writer, vmaddr_offset); - try leb.writeULEB128(writer, stream.bytes_written); + try bw.writeLeb128(export_flags); + try bw.writeLeb128(vmaddr_offset); + try bw.writeLeb128(bw.count); } else { 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 next_node_offset = slice.items(.trie_offset)[edge.node]; 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 updated = offset_in_trie != trie_offset; 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 }; } @@ -223,12 +225,11 @@ pub fn deinit(self: *Trie, allocator: Allocator) void { } self.nodes.deinit(allocator); self.edges.deinit(allocator); - self.buffer.deinit(allocator); + allocator.free(self.buffer); } -pub fn write(self: Trie, writer: anytype) !void { - if (self.buffer.items.len == 0) return; - try writer.writeAll(self.buffer.items); +pub fn write(self: Trie, bw: *Writer) Writer.Error!void { + try bw.writeAll(self.buffer); } /// 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. /// This is one of the requirements of the MachO. /// 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 edges = slice.items(.edges)[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]; if (is_terminal) { - // Terminal node info: encode export flags and vmaddr offset of this symbol. - var info_buf: [@sizeOf(u64) * 2]u8 = undefined; - var info_stream = std.io.fixedBufferStream(&info_buf); + const start = bw.count; // TODO Implement for special flags. assert(export_flags & macho.EXPORT_SYMBOL_FLAGS_REEXPORT == 0 and export_flags & macho.EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER == 0); - try leb.writeULEB128(info_stream.writer(), export_flags); - try leb.writeULEB128(info_stream.writer(), vmaddr_offset); - + // Terminal node info: encode export flags and vmaddr offset of this symbol. + try bw.writeLeb128(export_flags); + try bw.writeLeb128(vmaddr_offset); // Encode the size of the terminal node info. - var size_buf: [@sizeOf(u64)]u8 = undefined; - 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]); + try bw.writeLeb128(bw.count - start); } else { // 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). - try writer.writeByte(@as(u8, @intCast(edges.items.len))); + // Write number of edges (max legal number of edges is 255). + try bw.writeByte(@intCast(edges.items.len)); for (edges.items) |edge_index| { const edge = self.edges.items[edge_index]; // Write edge label and offset to next node in trie. - try writer.writeAll(edge.label); - try writer.writeByte(0); - try leb.writeULEB128(writer, slice.items(.trie_offset)[edge.node]); + try bw.writeAll(edge.label); + try bw.writeByte(0); + try bw.writeLeb128(slice.items(.trie_offset)[edge.node]); } } @@ -414,8 +407,10 @@ const macho = std.macho; const mem = std.mem; const std = @import("std"); 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 MachO = @import("../../MachO.zig"); const Trie = @This(); diff --git a/src/link/MachO/dyld_info/bind.zig b/src/link/MachO/dyld_info/bind.zig index 328d6a402c..f6697b6275 100644 --- a/src/link/MachO/dyld_info/bind.zig +++ b/src/link/MachO/dyld_info/bind.zig @@ -1,7 +1,7 @@ pub const Entry = struct { target: MachO.Ref, offset: u64, - segment_id: u8, + segment_id: u4, addend: i64, pub fn lessThan(ctx: *MachO, entry: Entry, other: Entry) bool { @@ -20,14 +20,12 @@ pub const Bind = struct { entries: std.ArrayListUnmanaged(Entry) = .empty, buffer: std.ArrayListUnmanaged(u8) = .empty, - const Self = @This(); - - pub fn deinit(self: *Self, gpa: Allocator) void { - self.entries.deinit(gpa); - self.buffer.deinit(gpa); + pub fn deinit(bind: *Bind, gpa: Allocator) void { + bind.entries.deinit(gpa); + bind.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()); defer tracy.end(); @@ -56,15 +54,12 @@ pub const Bind = struct { const addend = rel.addend + rel.getRelocAddend(cpu_arch); const sym = rel.getTargetSymbol(atom.*, macho_file); 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), .offset = atom_addr + rel_offset - seg.vmaddr, .segment_id = seg_id, .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| { const sym = ref.getSymbol(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, .offset = addr - seg.vmaddr, .segment_id = seg_id, .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| { const sym = ref.getSymbol(macho_file).?; 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, .offset = addr - seg.vmaddr, .segment_id = seg_id, .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| { const sym = ref.getSymbol(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, .offset = addr - seg.vmaddr, .segment_id = seg_id, .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); - macho_file.dyld_info_cmd.bind_size = mem.alignForward(u32, @intCast(self.buffer.items.len), @alignOf(u64)); + try bind.finalize(gpa, macho_file); + 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 { - if (self.entries.items.len == 0) return; + fn finalize(bind: *Bind, gpa: Allocator, ctx: *MachO) !void { + 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", .{}); - 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 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; - try finalizeSegment(self.entries.items[start..i], ctx, writer); + try finalizeSegment(bind.entries.items[start..i], ctx, bw); seg_id = entry.segment_id; start = i; } - try finalizeSegment(self.entries.items[start..], ctx, writer); - try done(writer); + try finalizeSegment(bind.entries.items[start..], ctx, bw); + 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; const seg_id = entries[0].segment_id; - try setSegmentOffset(seg_id, 0, writer); + try setSegmentOffset(seg_id, 0, bw); var offset: u64 = 0; var addend: i64 = 0; @@ -175,15 +163,15 @@ pub const Bind = struct { if (target == null or !target.?.eql(current.target)) { switch (state) { .start => {}, - .bind_single => try doBind(writer), - .bind_times_skip => try doBindTimesSkip(count, skip, writer), + .bind_single => try doBind(bw), + .bind_times_skip => try doBindTimesSkip(count, skip, bw), } state = .start; target = current.target; const sym = current.target.getSymbol(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: { if (sym.flags.interposable) break :ord macho.BIND_SPECIAL_DYLIB_FLAT_LOOKUP; if (sym.flags.import) { @@ -195,13 +183,13 @@ pub const Bind = struct { break :ord macho.BIND_SPECIAL_DYLIB_SELF; }; - try setSymbol(name, flags, writer); - try setTypePointer(writer); - try setDylibOrdinal(ordinal, writer); + try setSymbol(name, flags, bw); + try setTypePointer(bw); + try setDylibOrdinal(ordinal, bw); if (current.addend != addend) { addend = current.addend; - try setAddend(addend, writer); + try setAddend(addend, bw); } } @@ -210,11 +198,11 @@ pub const Bind = struct { switch (state) { .start => { 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); } else if (current.offset > offset) { const delta = current.offset - offset; - try addAddr(delta, writer); + try addAddr(delta, bw); offset += delta; } state = .bind_single; @@ -223,7 +211,7 @@ pub const Bind = struct { }, .bind_single => { if (current.offset == offset) { - try doBind(writer); + try doBind(bw); state = .start; } else if (current.offset > offset) { const delta = current.offset - offset; @@ -237,9 +225,9 @@ pub const Bind = struct { if (current.offset < offset) { count -= 1; if (count == 1) { - try doBindAddAddr(skip, writer); + try doBindAddAddr(skip, bw); } else { - try doBindTimesSkip(count, skip, writer); + try doBindTimesSkip(count, skip, bw); } state = .start; offset = offset - (@sizeOf(u64) + skip); @@ -248,7 +236,7 @@ pub const Bind = struct { count += 1; offset += @sizeOf(u64) + skip; } else { - try doBindTimesSkip(count, skip, writer); + try doBindTimesSkip(count, skip, bw); state = .start; i -= 1; } @@ -258,13 +246,13 @@ pub const Bind = struct { switch (state) { .start => unreachable, - .bind_single => try doBind(writer), - .bind_times_skip => try doBindTimesSkip(count, skip, writer), + .bind_single => try doBind(bw), + .bind_times_skip => try doBindTimesSkip(count, skip, bw), } } - pub fn write(self: Self, writer: anytype) !void { - try writer.writeAll(self.buffer.items); + pub fn write(bind: Bind, bw: *Writer) Writer.Error!void { + try bw.writeAll(bind.buffer.items); } }; @@ -272,14 +260,12 @@ pub const WeakBind = struct { entries: std.ArrayListUnmanaged(Entry) = .empty, buffer: std.ArrayListUnmanaged(u8) = .empty, - const Self = @This(); - - pub fn deinit(self: *Self, gpa: Allocator) void { - self.entries.deinit(gpa); - self.buffer.deinit(gpa); + pub fn deinit(bind: *WeakBind, gpa: Allocator) void { + bind.entries.deinit(gpa); + bind.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()); defer tracy.end(); @@ -308,15 +294,12 @@ pub const WeakBind = struct { const addend = rel.addend + rel.getRelocAddend(cpu_arch); const sym = rel.getTargetSymbol(atom.*, macho_file); 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), .offset = atom_addr + rel_offset - seg.vmaddr, .segment_id = seg_id, .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| { const sym = ref.getSymbol(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, .offset = addr - seg.vmaddr, .segment_id = seg_id, .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| { const sym = ref.getSymbol(macho_file).?; const addr = sect.addr + idx * @sizeOf(u64); - const bind_entry = Entry{ + if (sym.flags.weak) (try bind.entries.addOne(gpa)).* = .{ .target = ref, .offset = addr - seg.vmaddr, .segment_id = seg_id, .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| { const sym = ref.getSymbol(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, .offset = addr - seg.vmaddr, .segment_id = seg_id, .addend = 0, }; - if (sym.flags.weak) { - try self.entries.append(gpa, entry); - } } } - try self.finalize(gpa, macho_file); - macho_file.dyld_info_cmd.weak_bind_size = mem.alignForward(u32, @intCast(self.buffer.items.len), @alignOf(u64)); + try bind.finalize(gpa, macho_file); + 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 { - if (self.entries.items.len == 0) return; + fn finalize(bind: *WeakBind, gpa: Allocator, ctx: *MachO) !void { + 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", .{}); - 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 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; - try finalizeSegment(self.entries.items[start..i], ctx, writer); + try finalizeSegment(bind.entries.items[start..i], ctx, bw); seg_id = entry.segment_id; start = i; } - try finalizeSegment(self.entries.items[start..], ctx, writer); - try done(writer); + try finalizeSegment(bind.entries.items[start..], ctx, bw); + 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; const seg_id = entries[0].segment_id; - try setSegmentOffset(seg_id, 0, writer); + try setSegmentOffset(seg_id, 0, bw); var offset: u64 = 0; var addend: i64 = 0; @@ -428,8 +404,8 @@ pub const WeakBind = struct { if (target == null or !target.?.eql(current.target)) { switch (state) { .start => {}, - .bind_single => try doBind(writer), - .bind_times_skip => try doBindTimesSkip(count, skip, writer), + .bind_single => try doBind(bw), + .bind_times_skip => try doBindTimesSkip(count, skip, bw), } state = .start; target = current.target; @@ -438,12 +414,12 @@ pub const WeakBind = struct { const name = sym.getName(ctx); const flags: u8 = 0; // TODO NON_WEAK_DEFINITION - try setSymbol(name, flags, writer); - try setTypePointer(writer); + try setSymbol(name, flags, bw); + try setTypePointer(bw); if (current.addend != addend) { addend = current.addend; - try setAddend(addend, writer); + try setAddend(addend, bw); } } @@ -452,11 +428,11 @@ pub const WeakBind = struct { switch (state) { .start => { 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); } else if (current.offset > offset) { const delta = current.offset - offset; - try addAddr(delta, writer); + try addAddr(delta, bw); offset += delta; } state = .bind_single; @@ -465,7 +441,7 @@ pub const WeakBind = struct { }, .bind_single => { if (current.offset == offset) { - try doBind(writer); + try doBind(bw); state = .start; } else if (current.offset > offset) { const delta = current.offset - offset; @@ -479,9 +455,9 @@ pub const WeakBind = struct { if (current.offset < offset) { count -= 1; if (count == 1) { - try doBindAddAddr(skip, writer); + try doBindAddAddr(skip, bw); } else { - try doBindTimesSkip(count, skip, writer); + try doBindTimesSkip(count, skip, bw); } state = .start; offset = offset - (@sizeOf(u64) + skip); @@ -490,7 +466,7 @@ pub const WeakBind = struct { count += 1; offset += @sizeOf(u64) + skip; } else { - try doBindTimesSkip(count, skip, writer); + try doBindTimesSkip(count, skip, bw); state = .start; i -= 1; } @@ -500,13 +476,13 @@ pub const WeakBind = struct { switch (state) { .start => unreachable, - .bind_single => try doBind(writer), - .bind_times_skip => try doBindTimesSkip(count, skip, writer), + .bind_single => try doBind(bw), + .bind_times_skip => try doBindTimesSkip(count, skip, bw), } } - pub fn write(self: Self, writer: anytype) !void { - try writer.writeAll(self.buffer.items); + pub fn write(bind: WeakBind, bw: *Writer) Writer.Error!void { + try bw.writeAll(bind.buffer.items); } }; @@ -515,15 +491,13 @@ pub const LazyBind = struct { buffer: std.ArrayListUnmanaged(u8) = .empty, offsets: std.ArrayListUnmanaged(u32) = .empty, - const Self = @This(); - - pub fn deinit(self: *Self, gpa: Allocator) void { - self.entries.deinit(gpa); - self.buffer.deinit(gpa); - self.offsets.deinit(gpa); + pub fn deinit(bind: *LazyBind, gpa: Allocator) void { + bind.entries.deinit(gpa); + bind.buffer.deinit(gpa); + bind.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()); defer tracy.end(); @@ -537,36 +511,35 @@ pub const LazyBind = struct { for (macho_file.stubs.symbols.items, 0..) |ref, idx| { const sym = ref.getSymbol(macho_file).?; 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, .offset = addr - seg.vmaddr, .segment_id = seg_id, .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); - macho_file.dyld_info_cmd.lazy_bind_size = mem.alignForward(u32, @intCast(self.buffer.items.len), @alignOf(u64)); + try bind.finalize(gpa, macho_file); + 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 { - try self.offsets.ensureTotalCapacityPrecise(gpa, self.entries.items.len); + fn finalize(bind: *LazyBind, gpa: Allocator, ctx: *MachO) !void { + 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", .{}); var addend: i64 = 0; - for (self.entries.items) |entry| { - self.offsets.appendAssumeCapacity(@intCast(self.buffer.items.len)); + for (bind.entries.items) |entry| { + bind.offsets.appendAssumeCapacity(@intCast(bind.buffer.items.len)); const sym = entry.target.getSymbol(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: { if (sym.flags.interposable) break :ord macho.BIND_SPECIAL_DYLIB_FLAT_LOOKUP; if (sym.flags.import) { @@ -578,121 +551,116 @@ pub const LazyBind = struct { break :ord macho.BIND_SPECIAL_DYLIB_SELF; }; - try setSegmentOffset(entry.segment_id, entry.offset, writer); - try setSymbol(name, flags, writer); - try setDylibOrdinal(ordinal, writer); + try setSegmentOffset(entry.segment_id, entry.offset, bw); + try setSymbol(name, flags, bw); + try setDylibOrdinal(ordinal, bw); if (entry.addend != addend) { - try setAddend(entry.addend, writer); + try setAddend(entry.addend, bw); addend = entry.addend; } - try doBind(writer); - try done(writer); + try doBind(bw); + try done(bw); } } - pub fn write(self: Self, writer: anytype) !void { - try writer.writeAll(self.buffer.items); + pub fn write(bind: LazyBind, bw: *Writer) Writer.Error!void { + 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 }); - try writer.writeByte(macho.BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB | @as(u4, @truncate(segment_id))); - try std.leb.writeUleb128(writer, offset); + try bw.writeByte(macho.BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB | segment_id); + 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 }); - try writer.writeByte(macho.BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM | @as(u4, @truncate(flags))); - try writer.writeAll(name); - try writer.writeByte(0); + try bw.writeByte(macho.BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM | flags); + try bw.writeAll(name); + 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}); - 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 { - if (ordinal <= 0) { - switch (ordinal) { - macho.BIND_SPECIAL_DYLIB_SELF, - macho.BIND_SPECIAL_DYLIB_MAIN_EXECUTABLE, - macho.BIND_SPECIAL_DYLIB_FLAT_LOOKUP, - => {}, - else => unreachable, // Invalid dylib special binding - } - log.debug(">>> set dylib special: {d}", .{ordinal}); - const cast = @as(u16, @bitCast(ordinal)); - try writer.writeByte(macho.BIND_OPCODE_SET_DYLIB_SPECIAL_IMM | @as(u4, @truncate(cast))); - } else { - const cast = @as(u16, @bitCast(ordinal)); - log.debug(">>> set dylib ordinal: {d}", .{ordinal}); - if (cast <= 0xf) { - try writer.writeByte(macho.BIND_OPCODE_SET_DYLIB_ORDINAL_IMM | @as(u4, @truncate(cast))); - } else { - try writer.writeByte(macho.BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB); - try std.leb.writeUleb128(writer, cast); - } +fn setDylibOrdinal(ordinal: i16, bw: *Writer) Writer.Error!void { + switch (ordinal) { + else => unreachable, // Invalid dylib special binding + macho.BIND_SPECIAL_DYLIB_SELF, + macho.BIND_SPECIAL_DYLIB_MAIN_EXECUTABLE, + macho.BIND_SPECIAL_DYLIB_FLAT_LOOKUP, + => { + log.debug(">>> set dylib special: {d}", .{ordinal}); + try bw.writeByte(macho.BIND_OPCODE_SET_DYLIB_SPECIAL_IMM | @as(u4, @bitCast(@as(i4, @intCast(ordinal))))); + }, + 1...std.math.maxInt(i16) => { + log.debug(">>> set dylib ordinal: {d}", .{ordinal}); + if (std.math.cast(u4, ordinal)) |imm| { + try bw.writeByte(macho.BIND_OPCODE_SET_DYLIB_ORDINAL_IMM | imm); + } else { + try bw.writeByte(macho.BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB); + 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}); - try writer.writeByte(macho.BIND_OPCODE_SET_ADDEND_SLEB); - try std.leb.writeIleb128(writer, addend); + try bw.writeByte(macho.BIND_OPCODE_SET_ADDEND_SLEB); + try bw.writeLeb128(addend); } -fn doBind(writer: anytype) !void { +fn doBind(bw: *Writer) Writer.Error!void { 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}); - if (std.mem.isAlignedGeneric(u64, addr, @sizeOf(u64))) { - const imm = @divExact(addr, @sizeOf(u64)); - if (imm <= 0xf) { - try writer.writeByte( - macho.BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED | @as(u4, @truncate(imm)), - ); - return; - } - } - try writer.writeByte(macho.BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB); - try std.leb.writeUleb128(writer, addr); + if (std.math.divExact(u64, addr, @sizeOf(u64))) |scaled| { + if (std.math.cast(u4, scaled)) |imm_scaled| return bw.writeByte( + macho.BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED | imm_scaled, + ); + } else |_| {} + try bw.writeByte(macho.BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB); + try bw.writeLeb128(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 }); - try writer.writeByte(macho.BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB); - try std.leb.writeUleb128(writer, count); - try std.leb.writeUleb128(writer, skip); + try bw.writeByte(macho.BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB); + try bw.writeLeb128(count); + 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}); - try writer.writeByte(macho.BIND_OPCODE_ADD_ADDR_ULEB); - try std.leb.writeUleb128(writer, addr); + try bw.writeByte(macho.BIND_OPCODE_ADD_ADDR_ULEB); + try bw.writeLeb128(addr); } -fn done(writer: anytype) !void { +fn done(bw: *Writer) Writer.Error!void { 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 leb = std.leb; const log = std.log.scoped(.link_dyld_info); const macho = std.macho; const mem = std.mem; const testing = std.testing; -const trace = @import("../../../tracy.zig").trace; -const std = @import("std"); +const Allocator = std.mem.Allocator; +const Writer = std.io.Writer; -const Allocator = mem.Allocator; +const trace = @import("../../../tracy.zig").trace; const File = @import("../file.zig").File; const MachO = @import("../../MachO.zig"); const Symbol = @import("../Symbol.zig"); diff --git a/src/link/MachO/eh_frame.zig b/src/link/MachO/eh_frame.zig index ccabffb1dc..0344115b74 100644 --- a/src/link/MachO/eh_frame.zig +++ b/src/link/MachO/eh_frame.zig @@ -12,36 +12,33 @@ pub const Cie = struct { const tracy = trace(@src()); defer tracy.end(); - const data = cie.getData(macho_file); - const aug = std.mem.sliceTo(@as([*:0]const u8, @ptrCast(data.ptr + 9)), 0); + var r: std.io.Reader = .fixed(cie.getData(macho_file)); + try r.discard(9); + const aug = try r.takeSentinel(0); if (aug[0] != 'z') return; // TODO should we error out? - var stream = std.io.fixedBufferStream(data[9 + aug.len + 1 ..]); - var creader = std.io.countingReader(stream.reader()); - const reader = creader.reader(); - - _ = 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 + _ = try r.takeLeb128(u64); // code alignment factor + _ = try r.takeLeb128(u64); // data alignment factor + _ = try r.takeLeb128(u64); // return address register + _ = try r.takeLeb128(u64); // augmentation data length for (aug[1..]) |ch| switch (ch) { 'R' => { - const enc = try reader.readByte(); + const enc = try r.takeByte(); if (enc != DW_EH_PE.pcrel | DW_EH_PE.absptr) { @panic("unexpected pointer encoding"); // TODO error } }, '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) { @panic("unexpected personality pointer encoding"); // TODO error } - _ = try reader.readInt(u32, .little); // personality pointer + _ = try r.takeInt(u32, .little); // personality pointer }, 'L' => { - const enc = try reader.readByte(); + const enc = try r.takeByte(); switch (enc & DW_EH_PE.type_mask) { DW_EH_PE.sdata4 => cie.lsda_size = .p32, DW_EH_PE.absptr => cie.lsda_size = .p64, @@ -81,46 +78,26 @@ pub const Cie = struct { return true; } - pub fn format( - 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) { + pub fn fmt(cie: Cie, macho_file: *MachO) std.fmt.Formatter(Format, Format.default) { return .{ .data = .{ .cie = cie, .macho_file = macho_file, } }; } - const FormatContext = struct { + const Format = struct { cie: Cie, macho_file: *MachO, - }; - fn format2( - ctx: FormatContext, - comptime unused_fmt_string: []const u8, - options: std.fmt.FormatOptions, - writer: anytype, - ) !void { - _ = unused_fmt_string; - _ = options; - const cie = ctx.cie; - try writer.print("@{x} : size({x})", .{ - cie.offset, - cie.getSize(), - }); - if (!cie.alive) try writer.writeAll(" : [*]"); - } + fn default(f: Format, w: *Writer) Writer.Error!void { + const cie = f.cie; + try w.print("@{x} : size({x})", .{ + cie.offset, + cie.getSize(), + }); + if (!cie.alive) try w.writeAll(" : [*]"); + } + }; pub const Index = u32; @@ -148,12 +125,16 @@ pub const Fde = struct { const tracy = trace(@src()); defer tracy.end(); - const data = fde.getData(macho_file); const object = fde.getObject(macho_file); 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 - 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); fde.atom = object.findAtom(taddr) orelse { 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)); // 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_index = for (object.cies.items, 0..) |cie, 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 if (cie.lsda_size) |lsda_size| { - var stream = std.io.fixedBufferStream(data[24..]); - var creader = std.io.countingReader(stream.reader()); - const reader = creader.reader(); - _ = try leb.readUleb128(u64, reader); // augmentation length - fde.lsda_ptr_offset = @intCast(creader.bytes_read + 24); + try br.discard(8); + _ = try br.takeLeb128(u64); // augmentation length + fde.lsda_ptr_offset = @intCast(br.seek); const lsda_ptr = switch (lsda_size) { - .p32 => try reader.readInt(i32, .little), - .p64 => try reader.readInt(i64, .little), + .p32 => try br.takeInt(i32, .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); fde.lsda = object.findAtom(lsda_addr) orelse { @@ -231,56 +209,35 @@ pub const Fde = struct { return fde.getObject(macho_file).getAtom(fde.lsda); } - pub fn format( - 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) { + pub fn fmt(fde: Fde, macho_file: *MachO) std.fmt.Formatter(Format, Format.default) { return .{ .data = .{ .fde = fde, .macho_file = macho_file, } }; } - const FormatContext = struct { + const Format = struct { fde: Fde, macho_file: *MachO, - }; - fn format2( - ctx: FormatContext, - comptime unused_fmt_string: []const u8, - options: std.fmt.FormatOptions, - 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.getSize(), - fde.cie, - fde.getAtom(macho_file).getName(macho_file), - }); - if (!fde.alive) try writer.writeAll(" : [*]"); - } + fn default(f: Format, w: *Writer) Writer.Error!void { + const fde = f.fde; + const macho_file = f.macho_file; + try w.print("@{x} : size({x}) : cie({d}) : {s}", .{ + fde.offset, + fde.getSize(), + fde.cie, + fde.getAtom(macho_file).getName(macho_file), + }); + if (!fde.alive) try w.writeAll(" : [*]"); + } + }; pub const Index = u32; }; pub const Iterator = struct { - data: []const u8, - pos: u32 = 0, + reader: *std.io.Reader, pub const Record = struct { tag: enum { fde, cie }, @@ -289,21 +246,19 @@ pub const Iterator = struct { }; pub fn next(it: *Iterator) !?Record { - if (it.pos >= it.data.len) return null; - - var stream = std.io.fixedBufferStream(it.data[it.pos..]); - const reader = stream.reader(); + const r = it.reader; + if (r.seek >= r.storageBuffer().len) return null; - const size = try reader.readInt(u32, .little); + const size = try r.takeInt(u32, .little); if (size == 0xFFFFFFFF) @panic("DWARF CFI is 32bit on macOS"); - const id = try reader.readInt(u32, .little); - const record = Record{ + const id = try r.takeInt(u32, .little); + const record: Record = .{ .tag = if (id == 0) .cie else .fde, - .offset = it.pos, + .offset = @intCast(r.seek), .size = size, }; - it.pos += size + 4; + try r.discard(size); return record; } @@ -545,6 +500,7 @@ const math = std.math; const mem = std.mem; const std = @import("std"); const trace = @import("../../tracy.zig").trace; +const Writer = std.io.Writer; const Allocator = std.mem.Allocator; const Atom = @import("Atom.zig"); diff --git a/src/link/MachO/file.zig b/src/link/MachO/file.zig index d5f8cbd6d0..fde8553260 100644 --- a/src/link/MachO/file.zig +++ b/src/link/MachO/file.zig @@ -14,12 +14,12 @@ pub const File = union(enum) { 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) { - .zig_object => |zo| try writer.writeAll(zo.basename), - .internal => try writer.writeAll("internal"), - .object => |x| try writer.print("{}", .{x.fmtPath()}), - .dylib => |dl| try writer.print("{}", .{@as(Path, dl.path)}), + .zig_object => |zo| try w.writeAll(zo.basename), + .internal => try w.writeAll("internal"), + .object => |x| try w.print("{f}", .{x.fmtPath()}), + .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) { .dylib, .internal => unreachable, - .zig_object => |x| x.writeAr(ar_format, writer), - .object => |x| x.writeAr(ar_format, macho_file, writer), + .zig_object => |x| x.writeAr(bw, ar_format), + .object => |x| x.writeAr(bw, ar_format, macho_file), }; } @@ -364,6 +364,7 @@ const log = std.log.scoped(.link); const macho = std.macho; const Allocator = std.mem.Allocator; const Path = std.Build.Cache.Path; +const Writer = std.io.Writer; const trace = @import("../../tracy.zig").trace; const Archive = @import("Archive.zig"); diff --git a/src/link/MachO/load_commands.zig b/src/link/MachO/load_commands.zig index 08ab11e3f9..2d3f0bb231 100644 --- a/src/link/MachO/load_commands.zig +++ b/src/link/MachO/load_commands.zig @@ -3,6 +3,7 @@ const assert = std.debug.assert; const log = std.log.scoped(.link); const macho = std.macho; const mem = std.mem; +const Writer = std.io.Writer; const Allocator = mem.Allocator; const DebugSymbols = @import("DebugSymbols.zig"); @@ -180,23 +181,20 @@ pub fn calcMinHeaderPadSize(macho_file: *MachO) !u32 { 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 cmdsize = @as(u32, @intCast(mem.alignForward( u64, @sizeOf(macho.dylinker_command) + name_len, @sizeOf(u64), ))); - try writer.writeStruct(macho.dylinker_command{ + try bw.writeStruct(macho.dylinker_command{ .cmd = .LOAD_DYLINKER, .cmdsize = cmdsize, .name = @sizeOf(macho.dylinker_command), }); - try writer.writeAll(mem.sliceTo(default_dyld_path, 0)); - const padding = cmdsize - @sizeOf(macho.dylinker_command) - name_len; - if (padding > 0) { - try writer.writeByteNTimes(0, padding); - } + try bw.writeAll(mem.sliceTo(default_dyld_path, 0)); + try bw.splatByteAll(0, cmdsize - @sizeOf(macho.dylinker_command) - name_len); } const WriteDylibLCCtx = struct { @@ -207,14 +205,14 @@ const WriteDylibLCCtx = struct { 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 cmdsize = @as(u32, @intCast(mem.alignForward( + const cmdsize: u32 = @intCast(mem.alignForward( u64, @sizeOf(macho.dylib_command) + name_len, @sizeOf(u64), - ))); - try writer.writeStruct(macho.dylib_command{ + )); + try bw.writeStruct(macho.dylib_command{ .cmd = ctx.cmd, .cmdsize = cmdsize, .dylib = .{ @@ -224,12 +222,9 @@ pub fn writeDylibLC(ctx: WriteDylibLCCtx, writer: anytype) !void { .compatibility_version = ctx.compatibility_version, }, }); - try writer.writeAll(ctx.name); - try writer.writeByte(0); - const padding = cmdsize - @sizeOf(macho.dylib_command) - name_len; - if (padding > 0) { - try writer.writeByteNTimes(0, padding); - } + try bw.writeAll(ctx.name); + try bw.writeByte(0); + try bw.splatByteAll(0, cmdsize - @sizeOf(macho.dylib_command) - name_len); } pub fn writeDylibIdLC(macho_file: *MachO, writer: anytype) !void { @@ -258,26 +253,23 @@ pub fn writeDylibIdLC(macho_file: *MachO, writer: anytype) !void { }, 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 cmdsize = @as(u32, @intCast(mem.alignForward( u64, @sizeOf(macho.rpath_command) + rpath_len, @sizeOf(u64), ))); - try writer.writeStruct(macho.rpath_command{ + try bw.writeStruct(macho.rpath_command{ .cmdsize = cmdsize, .path = @sizeOf(macho.rpath_command), }); - try writer.writeAll(rpath); - try writer.writeByte(0); - const padding = cmdsize - @sizeOf(macho.rpath_command) - rpath_len; - if (padding > 0) { - try writer.writeByteNTimes(0, padding); - } + try bw.writeAll(rpath); + try bw.writeByte(0); + try bw.splatByteAll(0, cmdsize - @sizeOf(macho.rpath_command) - rpath_len); } -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) { .macos => .VERSION_MIN_MACOSX, .ios => .VERSION_MIN_IPHONEOS, @@ -285,7 +277,7 @@ pub fn writeVersionMinLC(platform: MachO.Platform, sdk_version: ?std.SemanticVer .watchos => .VERSION_MIN_WATCHOS, else => unreachable, }; - try writer.writeAll(mem.asBytes(&macho.version_min_command{ + try bw.writeAll(mem.asBytes(&macho.version_min_command{ .cmd = cmd, .version = platform.toAppleVersion(), .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); - try writer.writeStruct(macho.build_version_command{ + try bw.writeStruct(macho.build_version_command{ .cmdsize = cmdsize, .platform = platform.toApplePlatform(), .minos = platform.toAppleVersion(), @@ -307,7 +299,7 @@ pub fn writeBuildVersionLC(platform: MachO.Platform, sdk_version: ?std.SemanticV platform.toAppleVersion(), .ntools = 1, }); - try writer.writeAll(mem.asBytes(&macho.build_tool_version{ + try bw.writeAll(mem.asBytes(&macho.build_tool_version{ .tool = .ZIG, .version = 0x0, })); diff --git a/src/link/MachO/relocatable.zig b/src/link/MachO/relocatable.zig index 4edf6e043d..d0b7a24926 100644 --- a/src/link/MachO/relocatable.zig +++ b/src/link/MachO/relocatable.zig @@ -20,13 +20,13 @@ pub fn flushObject(macho_file: *MachO, comp: *Compilation, module_obj_path: ?Pat // the *only* input file over. const path = positionals.items[0].path().?; 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| - 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| - 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) - 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; } @@ -62,7 +62,7 @@ pub fn flushObject(macho_file: *MachO, comp: *Compilation, module_obj_path: ?Pat allocateSegment(macho_file); if (build_options.enable_logging) { - state_log.debug("{}", .{macho_file.dumpState()}); + state_log.debug("{f}", .{macho_file.dumpState()}); } try writeSections(macho_file); @@ -126,7 +126,7 @@ pub fn flushStaticLib(macho_file: *MachO, comp: *Compilation, module_obj_path: ? allocateSegment(macho_file); if (build_options.enable_logging) { - state_log.debug("{}", .{macho_file.dumpState()}); + state_log.debug("{f}", .{macho_file.dumpState()}); } try writeSections(macho_file); @@ -202,38 +202,30 @@ pub fn flushStaticLib(macho_file: *MachO, comp: *Compilation, module_obj_path: ? }; 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); - defer buffer.deinit(); - try buffer.ensureTotalCapacityPrecise(total_size); - const writer = buffer.writer(); + var bw: Writer = .fixed(try gpa.alloc(u8, total_size)); + defer gpa.free(bw.buffer); // Write magic - try writer.writeAll(Archive.ARMAG); + bw.writeAll(Archive.ARMAG) catch unreachable; // Write symtab - ar_symtab.write(format, macho_file, writer) catch |err| switch (err) { - error.OutOfMemory => return error.OutOfMemory, - else => |e| return diags.fail("failed to write archive symbol table: {s}", .{@errorName(e)}), + ar_symtab.write(&bw, format, macho_file) catch |err| { + return diags.fail("failed to write archive symbol table: {s}", .{@errorName(err)}); }; // Write object files for (files.items) |index| { - const aligned = mem.alignForward(usize, buffer.items.len, 2); - const padding = aligned - buffer.items.len; - if (padding > 0) { - try writer.writeByteNTimes(0, padding); - } - macho_file.getFile(index).?.writeAr(format, macho_file, writer) catch |err| + bw.splatByteAll(0, mem.alignForward(usize, bw.end, 2) - bw.end) catch unreachable; + macho_file.getFile(index).?.writeAr(&bw, format, macho_file) catch |err| return diags.fail("failed to write archive: {s}", .{@errorName(err)}); } - assert(buffer.items.len == total_size); - - try macho_file.setEndPos(total_size); - try macho_file.pwriteAll(buffer.items, 0); + assert(bw.end == bw.buffer.len); + try macho_file.setEndPos(bw.end); + try macho_file.pwriteAll(bw.buffer, 0); 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)}); } -fn writeSectionsToFile(macho_file: *MachO) !void { +fn writeSectionsToFile(macho_file: *MachO) link.File.FlushError!void { const tracy = trace(@src()); defer tracy.end(); @@ -689,12 +681,8 @@ fn writeSectionsToFile(macho_file: *MachO) !void { fn writeLoadCommands(macho_file: *MachO) error{ LinkFailure, OutOfMemory }!struct { usize, usize } { const gpa = macho_file.base.comp.gpa; - const needed_size = load_commands.calcLoadCommandsSizeObject(macho_file); - const buffer = try gpa.alloc(u8, needed_size); - defer gpa.free(buffer); - - var stream = std.io.fixedBufferStream(buffer); - const writer = stream.writer(); + var bw: Writer = .fixed(try gpa.alloc(u8, load_commands.calcLoadCommandsSizeObject(macho_file))); + defer gpa.free(bw.buffer); var ncmds: usize = 0; @@ -702,47 +690,31 @@ fn writeLoadCommands(macho_file: *MachO) error{ LinkFailure, OutOfMemory }!struc { assert(macho_file.segments.items.len == 1); const seg = macho_file.segments.items[0]; - writer.writeStruct(seg) catch |err| switch (err) { - error.NoSpaceLeft => unreachable, - }; + bw.writeStruct(seg) catch unreachable; for (macho_file.sections.items(.header)) |header| { - writer.writeStruct(header) catch |err| switch (err) { - error.NoSpaceLeft => unreachable, - }; + bw.writeStruct(header) catch unreachable; } ncmds += 1; } - writer.writeStruct(macho_file.data_in_code_cmd) catch |err| switch (err) { - error.NoSpaceLeft => unreachable, - }; + bw.writeStruct(macho_file.data_in_code_cmd) catch unreachable; ncmds += 1; - writer.writeStruct(macho_file.symtab_cmd) catch |err| switch (err) { - error.NoSpaceLeft => unreachable, - }; + bw.writeStruct(macho_file.symtab_cmd) catch unreachable; ncmds += 1; - writer.writeStruct(macho_file.dysymtab_cmd) catch |err| switch (err) { - error.NoSpaceLeft => unreachable, - }; + bw.writeStruct(macho_file.dysymtab_cmd) catch unreachable; ncmds += 1; if (macho_file.platform.isBuildVersionCompatible()) { - load_commands.writeBuildVersionLC(macho_file.platform, macho_file.sdk_version, writer) catch |err| switch (err) { - error.NoSpaceLeft => unreachable, - }; + load_commands.writeBuildVersionLC(&bw, macho_file.platform, macho_file.sdk_version) catch unreachable; ncmds += 1; } else { - load_commands.writeVersionMinLC(macho_file.platform, macho_file.sdk_version, writer) catch |err| switch (err) { - error.NoSpaceLeft => unreachable, - }; + load_commands.writeVersionMinLC(&bw, macho_file.platform, macho_file.sdk_version) catch unreachable; ncmds += 1; } - assert(stream.pos == needed_size); - - try macho_file.pwriteAll(buffer, @sizeOf(macho.mach_header_64)); - - return .{ ncmds, buffer.len }; + assert(bw.end == bw.buffer.len); + try macho_file.pwriteAll(bw.buffer, @sizeOf(macho.mach_header_64)); + return .{ ncmds, bw.end }; } fn writeHeader(macho_file: *MachO, ncmds: usize, sizeofcmds: usize) !void { @@ -784,6 +756,7 @@ const macho = std.macho; const math = std.math; const mem = std.mem; const state_log = std.log.scoped(.link_state); +const Writer = std.io.Writer; const Archive = @import("Archive.zig"); const Atom = @import("Atom.zig"); diff --git a/src/link/MachO/synthetic.zig b/src/link/MachO/synthetic.zig index 6042fe628d..6ac4860103 100644 --- a/src/link/MachO/synthetic.zig +++ b/src/link/MachO/synthetic.zig @@ -27,44 +27,37 @@ pub const GotSection = struct { 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()); defer tracy.end(); for (got.symbols.items) |ref| { const sym = ref.getSymbol(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, macho_file: *MachO, + + pub fn print(f: Format, w: *Writer) Writer.Error!void { + 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", .{ + i, + symbol.getGotAddress(f.macho_file), + ref, + symbol.getAddress(.{}, f.macho_file), + symbol.getName(f.macho_file), + }); + } + } }; - pub fn fmt(got: GotSection, macho_file: *MachO) std.fmt.Formatter(format2) { + pub fn fmt(got: GotSection, macho_file: *MachO) std.fmt.Formatter(Format, Format.print) { return .{ .data = .{ .got = got, .macho_file = macho_file } }; } - - 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, - symbol.getGotAddress(ctx.macho_file), - ref, - symbol.getAddress(.{}, ctx.macho_file), - symbol.getName(ctx.macho_file), - }); - } - } }; pub const StubsSection = struct { @@ -96,7 +89,7 @@ pub const StubsSection = struct { 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()); defer tracy.end(); const cpu_arch = macho_file.getTarget().cpu.arch; @@ -108,54 +101,47 @@ pub const StubsSection = struct { const target = laptr_sect.addr + idx * @sizeOf(u64); switch (cpu_arch) { .x86_64 => { - try writer.writeAll(&.{ 0xff, 0x25 }); - try writer.writeInt(i32, @intCast(target - source - 2 - 4), .little); + try bw.writeAll(&.{ 0xff, 0x25 }); + try bw.writeInt(i32, @intCast(target - source - 2 - 4), .little); }, .aarch64 => { // TODO relax if possible 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); - try writer.writeInt( + try bw.writeInt( u32, aarch64.Instruction.ldr(.x16, .x16, aarch64.Instruction.LoadStoreOffset.imm(off)).toU32(), .little, ); - try writer.writeInt(u32, aarch64.Instruction.br(.x16).toU32(), .little); + try bw.writeInt(u32, aarch64.Instruction.br(.x16).toU32(), .little); }, else => unreachable, } } } - const FormatCtx = struct { - stubs: StubsSection, - macho_file: *MachO, - }; - - pub fn fmt(stubs: StubsSection, macho_file: *MachO) std.fmt.Formatter(format2) { + pub fn fmt(stubs: StubsSection, macho_file: *MachO) std.fmt.Formatter(Format, Format.print) { return .{ .data = .{ .stubs = stubs, .macho_file = macho_file } }; } - pub fn format2( - ctx: FormatCtx, - comptime unused_fmt_string: []const u8, - options: std.fmt.FormatOptions, - writer: anytype, - ) !void { - _ = options; - _ = unused_fmt_string; - 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, - symbol.getStubsAddress(ctx.macho_file), - ref, - symbol.getAddress(.{}, ctx.macho_file), - symbol.getName(ctx.macho_file), - }); + const Format = struct { + stubs: StubsSection, + macho_file: *MachO, + + pub fn print(f: Format, w: *Writer) Writer.Error!void { + for (f.stubs.symbols.items, 0..) |ref, i| { + const symbol = ref.getSymbol(f.macho_file).?; + try w.print(" {d}@0x{x} => {f}@0x{x} ({s})\n", .{ + i, + symbol.getStubsAddress(f.macho_file), + ref, + symbol.getAddress(.{}, f.macho_file), + symbol.getName(f.macho_file), + }); + } } - } + }; }; pub const StubsHelperSection = struct { @@ -189,11 +175,11 @@ pub const StubsHelperSection = struct { 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()); 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 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); switch (cpu_arch) { .x86_64 => { - try writer.writeByte(0x68); - try writer.writeInt(u32, offset, .little); - try writer.writeByte(0xe9); - try writer.writeInt(i32, @intCast(target - source - 6 - 4), .little); + try bw.writeByte(0x68); + try bw.writeInt(u32, offset, .little); + try bw.writeByte(0xe9); + try bw.writeInt(i32, @intCast(target - source - 6 - 4), .little); }, .aarch64 => { const literal = blk: { 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; }; - try writer.writeInt(u32, aarch64.Instruction.ldrLiteral( + try bw.writeInt(u32, aarch64.Instruction.ldrLiteral( .w16, literal, ).toU32(), .little); const disp = math.cast(i28, @as(i64, @intCast(target)) - @as(i64, @intCast(source + 4))) orelse return error.Overflow; - try writer.writeInt(u32, aarch64.Instruction.b(disp).toU32(), .little); - try writer.writeAll(&.{ 0x0, 0x0, 0x0, 0x0 }); + try bw.writeInt(u32, aarch64.Instruction.b(disp).toU32(), .little); + try bw.writeAll(&.{ 0x0, 0x0, 0x0, 0x0 }); }, 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; const obj = macho_file.getInternalObject().?; const cpu_arch = macho_file.getTarget().cpu.arch; @@ -249,21 +235,21 @@ pub const StubsHelperSection = struct { }; switch (cpu_arch) { .x86_64 => { - try writer.writeAll(&.{ 0x4c, 0x8d, 0x1d }); - try writer.writeInt(i32, @intCast(dyld_private_addr - sect.addr - 3 - 4), .little); - try writer.writeAll(&.{ 0x41, 0x53, 0xff, 0x25 }); - try writer.writeInt(i32, @intCast(dyld_stub_binder_addr - sect.addr - 11 - 4), .little); - try writer.writeByte(0x90); + try bw.writeAll(&.{ 0x4c, 0x8d, 0x1d }); + try bw.writeInt(i32, @intCast(dyld_private_addr - sect.addr - 3 - 4), .little); + try bw.writeAll(&.{ 0x41, 0x53, 0xff, 0x25 }); + try bw.writeInt(i32, @intCast(dyld_stub_binder_addr - sect.addr - 11 - 4), .little); + try bw.writeByte(0x90); }, .aarch64 => { { // TODO relax if possible 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); - 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, .x17, aarch64.Register.sp, @@ -272,15 +258,15 @@ pub const StubsHelperSection = struct { { // TODO relax if possible 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); - try writer.writeInt(u32, aarch64.Instruction.ldr( + try bw.writeInt(u32, aarch64.Instruction.ldr( .x16, .x16, aarch64.Instruction.LoadStoreOffset.imm(off), ).toU32(), .little); } - try writer.writeInt(u32, aarch64.Instruction.br(.x16).toU32(), .little); + try bw.writeInt(u32, aarch64.Instruction.br(.x16).toU32(), .little); }, else => unreachable, } @@ -293,7 +279,7 @@ pub const LaSymbolPtrSection = struct { 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()); defer tracy.end(); _ = laptr; @@ -304,12 +290,12 @@ pub const LaSymbolPtrSection = struct { const sym = ref.getSymbol(macho_file).?; if (sym.flags.weak) { const value = sym.getAddress(.{ .stubs = false }, macho_file); - try writer.writeInt(u64, @intCast(value), .little); + try bw.writeInt(u64, @intCast(value), .little); } else { const value = sect.addr + StubsHelperSection.preambleSize(cpu_arch) + StubsHelperSection.entrySize(cpu_arch) * stub_helper_idx; 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); } - 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()); defer tracy.end(); for (tlv.symbols.items) |ref| { const sym = ref.getSymbol(macho_file).?; if (sym.flags.import) { - try writer.writeInt(u64, 0, .little); + try bw.writeInt(u64, 0, .little); } else { - try writer.writeInt(u64, sym.getAddress(.{}, macho_file), .little); + try bw.writeInt(u64, sym.getAddress(.{}, macho_file), .little); } } } - const FormatCtx = struct { - tlv: TlvPtrSection, - macho_file: *MachO, - }; - - pub fn fmt(tlv: TlvPtrSection, macho_file: *MachO) std.fmt.Formatter(format2) { + pub fn fmt(tlv: TlvPtrSection, macho_file: *MachO) std.fmt.Formatter(Format, Format.print) { return .{ .data = .{ .tlv = tlv, .macho_file = macho_file } }; } - pub fn format2( - ctx: FormatCtx, - comptime unused_fmt_string: []const u8, - options: std.fmt.FormatOptions, - writer: anytype, - ) !void { - _ = options; - _ = unused_fmt_string; - 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, - symbol.getTlvPtrAddress(ctx.macho_file), - ref, - symbol.getAddress(.{}, ctx.macho_file), - symbol.getName(ctx.macho_file), - }); + const Format = struct { + tlv: TlvPtrSection, + macho_file: *MachO, + + pub fn print(f: Format, w: *Writer) Writer.Error!void { + for (f.tlv.symbols.items, 0..) |ref, i| { + const symbol = ref.getSymbol(f.macho_file).?; + try w.print(" {d}@0x{x} => {f}@0x{x} ({s})\n", .{ + i, + symbol.getTlvPtrAddress(f.macho_file), + ref, + symbol.getAddress(.{}, f.macho_file), + symbol.getName(f.macho_file), + }); + } } - } + }; }; pub const ObjcStubsSection = struct { @@ -421,7 +400,7 @@ pub const ObjcStubsSection = struct { 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()); defer tracy.end(); @@ -432,18 +411,18 @@ pub const ObjcStubsSection = struct { const addr = objc.getAddress(@intCast(idx), macho_file); switch (macho_file.getTarget().cpu.arch) { .x86_64 => { - try writer.writeAll(&.{ 0x48, 0x8b, 0x35 }); + try bw.writeAll(&.{ 0x48, 0x8b, 0x35 }); { const target = sym.getObjcSelrefsAddress(macho_file); 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 = target_sym.getGotAddress(macho_file); 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 => { @@ -451,9 +430,9 @@ pub const ObjcStubsSection = struct { const target = sym.getObjcSelrefsAddress(macho_file); const source = addr; 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); - try writer.writeInt( + try bw.writeInt( u32, aarch64.Instruction.ldr(.x1, .x1, aarch64.Instruction.LoadStoreOffset.imm(off)).toU32(), .little, @@ -464,52 +443,45 @@ pub const ObjcStubsSection = struct { const target = target_sym.getGotAddress(macho_file); const source = addr + 2 * @sizeOf(u32); 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); - try writer.writeInt( + try bw.writeInt( u32, aarch64.Instruction.ldr(.x16, .x16, aarch64.Instruction.LoadStoreOffset.imm(off)).toU32(), .little, ); } - try writer.writeInt(u32, aarch64.Instruction.br(.x16).toU32(), .little); - try writer.writeInt(u32, aarch64.Instruction.brk(1).toU32(), .little); - try writer.writeInt(u32, aarch64.Instruction.brk(1).toU32(), .little); - try writer.writeInt(u32, aarch64.Instruction.brk(1).toU32(), .little); + try bw.writeInt(u32, aarch64.Instruction.br(.x16).toU32(), .little); + try bw.writeInt(u32, aarch64.Instruction.brk(1).toU32(), .little); + try bw.writeInt(u32, aarch64.Instruction.brk(1).toU32(), .little); + try bw.writeInt(u32, aarch64.Instruction.brk(1).toU32(), .little); }, else => unreachable, } } } - const FormatCtx = struct { - objc: ObjcStubsSection, - macho_file: *MachO, - }; - - pub fn fmt(objc: ObjcStubsSection, macho_file: *MachO) std.fmt.Formatter(format2) { + pub fn fmt(objc: ObjcStubsSection, macho_file: *MachO) std.fmt.Formatter(Format, Format.print) { return .{ .data = .{ .objc = objc, .macho_file = macho_file } }; } - pub fn format2( - ctx: FormatCtx, - comptime unused_fmt_string: []const u8, - options: std.fmt.FormatOptions, - writer: anytype, - ) !void { - _ = options; - _ = unused_fmt_string; - 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, - symbol.getObjcStubsAddress(ctx.macho_file), - ref, - symbol.getAddress(.{}, ctx.macho_file), - symbol.getName(ctx.macho_file), - }); + const Format = struct { + objc: ObjcStubsSection, + macho_file: *MachO, + + pub fn print(f: Format, w: *Writer) Writer.Error!void { + for (f.objc.symbols.items, 0..) |ref, i| { + const symbol = ref.getSymbol(f.macho_file).?; + try w.print(" {d}@0x{x} => {f}@0x{x} ({s})\n", .{ + i, + symbol.getObjcStubsAddress(f.macho_file), + ref, + symbol.getAddress(.{}, f.macho_file), + symbol.getName(f.macho_file), + }); + } } - } + }; pub const Index = u32; }; @@ -524,7 +496,7 @@ pub const Indsymtab = struct { 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()); defer tracy.end(); @@ -533,21 +505,21 @@ pub const Indsymtab = struct { for (macho_file.stubs.symbols.items) |ref| { const sym = ref.getSymbol(macho_file).?; 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| { const sym = ref.getSymbol(macho_file).?; 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| { const sym = ref.getSymbol(macho_file).?; 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; } - 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()) macho_file.getTextSegment().vmaddr else @@ -609,7 +581,7 @@ pub const DataInCode = struct { for (dice.entries.items) |entry| { const atom_address = entry.atom_ref.getAtom(macho_file).?.getAddress(macho_file); 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), .length = entry.length, .kind = entry.kind, @@ -625,13 +597,14 @@ pub const DataInCode = struct { }; }; +const std = @import("std"); const aarch64 = @import("../aarch64.zig"); const assert = std.debug.assert; const macho = std.macho; const math = std.math; -const std = @import("std"); -const trace = @import("../../tracy.zig").trace; - const Allocator = std.mem.Allocator; +const Writer = std.io.Writer; + +const trace = @import("../../tracy.zig").trace; const MachO = @import("../MachO.zig"); const Symbol = @import("Symbol.zig"); |
