Commit c8fcd2ff2c

Andrew Kelley <andrew@ziglang.org>
2025-07-02 03:14:45
MachO: update to new std.io APIs
1 parent f71d97e
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,
         };
     }
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");
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();
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();
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;
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");
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 });
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");
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");
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 {
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;
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");
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");
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");
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,
     }));
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;
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");
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");
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;
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");
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");
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");
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");
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");
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.
+        }
+    }
+}