aboutsummaryrefslogtreecommitdiff
path: root/src/link/MachO/dyld_info
diff options
context:
space:
mode:
authorAndrew Kelley <andrew@ziglang.org>2025-07-01 18:14:45 -0700
committerAndrew Kelley <andrew@ziglang.org>2025-07-07 22:43:52 -0700
commitc8fcd2ff2c032b2de8cc1a57e075552d1cab35df (patch)
tree7155f58049ecd0e948533f6b64cba1553dd33ae2 /src/link/MachO/dyld_info
parentf71d97e4cbb0e56213cb76657ad6c9edf6134868 (diff)
downloadzig-c8fcd2ff2c032b2de8cc1a57e075552d1cab35df.tar.gz
zig-c8fcd2ff2c032b2de8cc1a57e075552d1cab35df.zip
MachO: update to new std.io APIs
Diffstat (limited to 'src/link/MachO/dyld_info')
-rw-r--r--src/link/MachO/dyld_info/Rebase.zig95
-rw-r--r--src/link/MachO/dyld_info/Trie.zig69
-rw-r--r--src/link/MachO/dyld_info/bind.zig342
3 files changed, 235 insertions, 271 deletions
diff --git a/src/link/MachO/dyld_info/Rebase.zig b/src/link/MachO/dyld_info/Rebase.zig
index dc4fcbcc1d..5fdba220a7 100644
--- a/src/link/MachO/dyld_info/Rebase.zig
+++ b/src/link/MachO/dyld_info/Rebase.zig
@@ -3,7 +3,7 @@ buffer: std.ArrayListUnmanaged(u8) = .empty,
pub const Entry = struct {
offset: u64,
- segment_id: u8,
+ segment_id: u4,
pub fn lessThan(ctx: void, entry: Entry, other: Entry) bool {
_ = ctx;
@@ -110,33 +110,35 @@ pub fn updateSize(rebase: *Rebase, macho_file: *MachO) !void {
fn finalize(rebase: *Rebase, gpa: Allocator) !void {
if (rebase.entries.items.len == 0) return;
- const writer = rebase.buffer.writer(gpa);
+ var aw: std.io.Writer.Allocating = .fromArrayList(gpa, &rebase.buffer);
+ const bw = &aw.writer;
+ defer rebase.buffer = aw.toArrayList();
log.debug("rebase opcodes", .{});
std.mem.sort(Entry, rebase.entries.items, {}, Entry.lessThan);
- try setTypePointer(writer);
+ try setTypePointer(bw);
var start: usize = 0;
var seg_id: ?u8 = null;
for (rebase.entries.items, 0..) |entry, i| {
if (seg_id != null and seg_id.? == entry.segment_id) continue;
- try finalizeSegment(rebase.entries.items[start..i], writer);
+ try finalizeSegment(rebase.entries.items[start..i], bw);
seg_id = entry.segment_id;
start = i;
}
- try finalizeSegment(rebase.entries.items[start..], writer);
- try done(writer);
+ try finalizeSegment(rebase.entries.items[start..], bw);
+ try done(bw);
}
-fn finalizeSegment(entries: []const Entry, writer: anytype) !void {
+fn finalizeSegment(entries: []const Entry, bw: *Writer) Writer.Error!void {
if (entries.len == 0) return;
const segment_id = entries[0].segment_id;
var offset = entries[0].offset;
- try setSegmentOffset(segment_id, offset, writer);
+ try setSegmentOffset(segment_id, offset, bw);
var count: usize = 0;
var skip: u64 = 0;
@@ -155,7 +157,7 @@ fn finalizeSegment(entries: []const Entry, writer: anytype) !void {
.start => {
if (offset < current_offset) {
const delta = current_offset - offset;
- try addAddr(delta, writer);
+ try addAddr(delta, bw);
offset += delta;
}
state = .times;
@@ -175,7 +177,7 @@ fn finalizeSegment(entries: []const Entry, writer: anytype) !void {
offset += skip;
i -= 1;
} else {
- try rebaseTimes(count, writer);
+ try rebaseTimes(count, bw);
state = .start;
i -= 1;
}
@@ -184,9 +186,9 @@ fn finalizeSegment(entries: []const Entry, writer: anytype) !void {
if (current_offset < offset) {
count -= 1;
if (count == 1) {
- try rebaseAddAddr(skip, writer);
+ try rebaseAddAddr(skip, bw);
} else {
- try rebaseTimesSkip(count, skip, writer);
+ try rebaseTimesSkip(count, skip, bw);
}
state = .start;
offset = offset - (@sizeOf(u64) + skip);
@@ -199,7 +201,7 @@ fn finalizeSegment(entries: []const Entry, writer: anytype) !void {
count += 1;
offset += @sizeOf(u64) + skip;
} else {
- try rebaseTimesSkip(count, skip, writer);
+ try rebaseTimesSkip(count, skip, bw);
state = .start;
i -= 1;
}
@@ -210,68 +212,66 @@ fn finalizeSegment(entries: []const Entry, writer: anytype) !void {
switch (state) {
.start => unreachable,
.times => {
- try rebaseTimes(count, writer);
+ try rebaseTimes(count, bw);
},
.times_skip => {
- try rebaseTimesSkip(count, skip, writer);
+ try rebaseTimesSkip(count, skip, bw);
},
}
}
-fn setTypePointer(writer: anytype) !void {
+fn setTypePointer(bw: *Writer) Writer.Error!void {
log.debug(">>> set type: {d}", .{macho.REBASE_TYPE_POINTER});
- try writer.writeByte(macho.REBASE_OPCODE_SET_TYPE_IMM | @as(u4, @truncate(macho.REBASE_TYPE_POINTER)));
+ try bw.writeByte(macho.REBASE_OPCODE_SET_TYPE_IMM | @as(u4, @intCast(macho.REBASE_TYPE_POINTER)));
}
-fn setSegmentOffset(segment_id: u8, offset: u64, writer: anytype) !void {
+fn setSegmentOffset(segment_id: u4, offset: u64, bw: *Writer) Writer.Error!void {
log.debug(">>> set segment: {d} and offset: {x}", .{ segment_id, offset });
- try writer.writeByte(macho.REBASE_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB | @as(u4, @truncate(segment_id)));
- try std.leb.writeUleb128(writer, offset);
+ try bw.writeByte(macho.REBASE_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB | @as(u4, @truncate(segment_id)));
+ try bw.writeLeb128(offset);
}
-fn rebaseAddAddr(addr: u64, writer: anytype) !void {
+fn rebaseAddAddr(addr: u64, bw: *Writer) Writer.Error!void {
log.debug(">>> rebase with add: {x}", .{addr});
- try writer.writeByte(macho.REBASE_OPCODE_DO_REBASE_ADD_ADDR_ULEB);
- try std.leb.writeUleb128(writer, addr);
+ try bw.writeByte(macho.REBASE_OPCODE_DO_REBASE_ADD_ADDR_ULEB);
+ try bw.writeLeb128(addr);
}
-fn rebaseTimes(count: usize, writer: anytype) !void {
+fn rebaseTimes(count: usize, bw: *Writer) Writer.Error!void {
log.debug(">>> rebase with count: {d}", .{count});
if (count <= 0xf) {
- try writer.writeByte(macho.REBASE_OPCODE_DO_REBASE_IMM_TIMES | @as(u4, @truncate(count)));
+ try bw.writeByte(macho.REBASE_OPCODE_DO_REBASE_IMM_TIMES | @as(u4, @truncate(count)));
} else {
- try writer.writeByte(macho.REBASE_OPCODE_DO_REBASE_ULEB_TIMES);
- try std.leb.writeUleb128(writer, count);
+ try bw.writeByte(macho.REBASE_OPCODE_DO_REBASE_ULEB_TIMES);
+ try bw.writeLeb128(count);
}
}
-fn rebaseTimesSkip(count: usize, skip: u64, writer: anytype) !void {
+fn rebaseTimesSkip(count: usize, skip: u64, bw: *Writer) Writer.Error!void {
log.debug(">>> rebase with count: {d} and skip: {x}", .{ count, skip });
- try writer.writeByte(macho.REBASE_OPCODE_DO_REBASE_ULEB_TIMES_SKIPPING_ULEB);
- try std.leb.writeUleb128(writer, count);
- try std.leb.writeUleb128(writer, skip);
+ try bw.writeByte(macho.REBASE_OPCODE_DO_REBASE_ULEB_TIMES_SKIPPING_ULEB);
+ try bw.writeLeb128(count);
+ try bw.writeLeb128(skip);
}
-fn addAddr(addr: u64, writer: anytype) !void {
+fn addAddr(addr: u64, bw: *Writer) Writer.Error!void {
log.debug(">>> add: {x}", .{addr});
- if (std.mem.isAlignedGeneric(u64, addr, @sizeOf(u64))) {
- const imm = @divExact(addr, @sizeOf(u64));
- if (imm <= 0xf) {
- try writer.writeByte(macho.REBASE_OPCODE_ADD_ADDR_IMM_SCALED | @as(u4, @truncate(imm)));
- return;
- }
- }
- try writer.writeByte(macho.REBASE_OPCODE_ADD_ADDR_ULEB);
- try std.leb.writeUleb128(writer, addr);
+ if (std.math.divExact(u64, addr, @sizeOf(u64))) |scaled| {
+ if (std.math.cast(u4, scaled)) |imm_scaled| return bw.writeByte(
+ macho.REBASE_OPCODE_ADD_ADDR_IMM_SCALED | imm_scaled,
+ );
+ } else |_| {}
+ try bw.writeByte(macho.REBASE_OPCODE_ADD_ADDR_ULEB);
+ try bw.writeLeb128(addr);
}
-fn done(writer: anytype) !void {
+fn done(bw: *Writer) Writer.Error!void {
log.debug(">>> done", .{});
- try writer.writeByte(macho.REBASE_OPCODE_DONE);
+ try bw.writeByte(macho.REBASE_OPCODE_DONE);
}
-pub fn write(rebase: Rebase, writer: anytype) !void {
- try writer.writeAll(rebase.buffer.items);
+pub fn write(rebase: Rebase, bw: *Writer) Writer.Error!void {
+ try bw.writeAll(rebase.buffer.items);
}
test "rebase - no entries" {
@@ -654,9 +654,10 @@ const log = std.log.scoped(.link_dyld_info);
const macho = std.macho;
const mem = std.mem;
const testing = std.testing;
-const trace = @import("../../../tracy.zig").trace;
-
const Allocator = mem.Allocator;
+const Writer = std.io.Writer;
+
+const trace = @import("../../../tracy.zig").trace;
const File = @import("../file.zig").File;
const MachO = @import("../../MachO.zig");
const Rebase = @This();
diff --git a/src/link/MachO/dyld_info/Trie.zig b/src/link/MachO/dyld_info/Trie.zig
index 8224dc8424..841bb9541b 100644
--- a/src/link/MachO/dyld_info/Trie.zig
+++ b/src/link/MachO/dyld_info/Trie.zig
@@ -31,7 +31,7 @@
/// The root node of the trie.
root: ?Node.Index = null,
-buffer: std.ArrayListUnmanaged(u8) = .empty,
+buffer: []u8 = &.{},
nodes: std.MultiArrayList(Node) = .{},
edges: std.ArrayListUnmanaged(Edge) = .empty,
@@ -123,7 +123,7 @@ pub fn updateSize(self: *Trie, macho_file: *MachO) !void {
try self.finalize(gpa);
- macho_file.dyld_info_cmd.export_size = mem.alignForward(u32, @intCast(self.buffer.items.len), @alignOf(u64));
+ macho_file.dyld_info_cmd.export_size = mem.alignForward(u32, @intCast(self.buffer.len), @alignOf(u64));
}
/// Finalizes this trie for writing to a byte stream.
@@ -138,7 +138,7 @@ fn finalize(self: *Trie, allocator: Allocator) !void {
defer ordered_nodes.deinit();
try ordered_nodes.ensureTotalCapacityPrecise(self.nodes.items(.is_terminal).len);
- var fifo = std.fifo.LinearFifo(Node.Index, .Dynamic).init(allocator);
+ var fifo = DeprecatedLinearFifo(Node.Index).init(allocator);
defer fifo.deinit();
try fifo.writeItem(self.root.?);
@@ -164,9 +164,11 @@ fn finalize(self: *Trie, allocator: Allocator) !void {
}
}
- try self.buffer.ensureTotalCapacityPrecise(allocator, size);
+ assert(self.buffer.len == 0);
+ self.buffer = try allocator.alloc(u8, size);
+ var bw: Writer = .fixed(self.buffer);
for (ordered_nodes.items) |node_index| {
- try self.writeNode(node_index, self.buffer.writer(allocator));
+ try self.writeNode(node_index, &bw);
}
}
@@ -181,17 +183,17 @@ const FinalizeNodeResult = struct {
/// Updates offset of this node in the output byte stream.
fn finalizeNode(self: *Trie, node_index: Node.Index, offset_in_trie: u32) !FinalizeNodeResult {
- var stream = std.io.countingWriter(std.io.null_writer);
- const writer = stream.writer();
+ var buf: [1024]u8 = undefined;
+ var bw: Writer = .discarding(&buf);
const slice = self.nodes.slice();
var node_size: u32 = 0;
if (slice.items(.is_terminal)[node_index]) {
const export_flags = slice.items(.export_flags)[node_index];
const vmaddr_offset = slice.items(.vmaddr_offset)[node_index];
- try leb.writeULEB128(writer, export_flags);
- try leb.writeULEB128(writer, vmaddr_offset);
- try leb.writeULEB128(writer, stream.bytes_written);
+ try bw.writeLeb128(export_flags);
+ try bw.writeLeb128(vmaddr_offset);
+ try bw.writeLeb128(bw.count);
} else {
node_size += 1; // 0x0 for non-terminal nodes
}
@@ -201,13 +203,13 @@ fn finalizeNode(self: *Trie, node_index: Node.Index, offset_in_trie: u32) !Final
const edge = &self.edges.items[edge_index];
const next_node_offset = slice.items(.trie_offset)[edge.node];
node_size += @intCast(edge.label.len + 1);
- try leb.writeULEB128(writer, next_node_offset);
+ try bw.writeLeb128(next_node_offset);
}
const trie_offset = slice.items(.trie_offset)[node_index];
const updated = offset_in_trie != trie_offset;
slice.items(.trie_offset)[node_index] = offset_in_trie;
- node_size += @intCast(stream.bytes_written);
+ node_size += @intCast(bw.count);
return .{ .node_size = node_size, .updated = updated };
}
@@ -223,12 +225,11 @@ pub fn deinit(self: *Trie, allocator: Allocator) void {
}
self.nodes.deinit(allocator);
self.edges.deinit(allocator);
- self.buffer.deinit(allocator);
+ allocator.free(self.buffer);
}
-pub fn write(self: Trie, writer: anytype) !void {
- if (self.buffer.items.len == 0) return;
- try writer.writeAll(self.buffer.items);
+pub fn write(self: Trie, bw: *Writer) Writer.Error!void {
+ try bw.writeAll(self.buffer);
}
/// Writes this node to a byte stream.
@@ -237,7 +238,7 @@ pub fn write(self: Trie, writer: anytype) !void {
/// iterate over `Trie.ordered_nodes` and call this method on each node.
/// This is one of the requirements of the MachO.
/// Panics if `finalize` was not called before calling this method.
-fn writeNode(self: *Trie, node_index: Node.Index, writer: anytype) !void {
+fn writeNode(self: *Trie, node_index: Node.Index, bw: *Writer) !void {
const slice = self.nodes.slice();
const edges = slice.items(.edges)[node_index];
const is_terminal = slice.items(.is_terminal)[node_index];
@@ -245,36 +246,28 @@ fn writeNode(self: *Trie, node_index: Node.Index, writer: anytype) !void {
const vmaddr_offset = slice.items(.vmaddr_offset)[node_index];
if (is_terminal) {
- // Terminal node info: encode export flags and vmaddr offset of this symbol.
- var info_buf: [@sizeOf(u64) * 2]u8 = undefined;
- var info_stream = std.io.fixedBufferStream(&info_buf);
+ const start = bw.count;
// TODO Implement for special flags.
assert(export_flags & macho.EXPORT_SYMBOL_FLAGS_REEXPORT == 0 and
export_flags & macho.EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER == 0);
- try leb.writeULEB128(info_stream.writer(), export_flags);
- try leb.writeULEB128(info_stream.writer(), vmaddr_offset);
-
+ // Terminal node info: encode export flags and vmaddr offset of this symbol.
+ try bw.writeLeb128(export_flags);
+ try bw.writeLeb128(vmaddr_offset);
// Encode the size of the terminal node info.
- var size_buf: [@sizeOf(u64)]u8 = undefined;
- var size_stream = std.io.fixedBufferStream(&size_buf);
- try leb.writeULEB128(size_stream.writer(), info_stream.pos);
-
- // Now, write them to the output stream.
- try writer.writeAll(size_buf[0..size_stream.pos]);
- try writer.writeAll(info_buf[0..info_stream.pos]);
+ try bw.writeLeb128(bw.count - start);
} else {
// Non-terminal node is delimited by 0 byte.
- try writer.writeByte(0);
+ try bw.writeByte(0);
}
- // Write number of edges (max legal number of edges is 256).
- try writer.writeByte(@as(u8, @intCast(edges.items.len)));
+ // Write number of edges (max legal number of edges is 255).
+ try bw.writeByte(@intCast(edges.items.len));
for (edges.items) |edge_index| {
const edge = self.edges.items[edge_index];
// Write edge label and offset to next node in trie.
- try writer.writeAll(edge.label);
- try writer.writeByte(0);
- try leb.writeULEB128(writer, slice.items(.trie_offset)[edge.node]);
+ try bw.writeAll(edge.label);
+ try bw.writeByte(0);
+ try bw.writeLeb128(slice.items(.trie_offset)[edge.node]);
}
}
@@ -414,8 +407,10 @@ const macho = std.macho;
const mem = std.mem;
const std = @import("std");
const testing = std.testing;
-const trace = @import("../../../tracy.zig").trace;
+const Writer = std.io.Writer;
+const trace = @import("../../../tracy.zig").trace;
+const DeprecatedLinearFifo = @import("../../../deprecated.zig").LinearFifo;
const Allocator = mem.Allocator;
const MachO = @import("../../MachO.zig");
const Trie = @This();
diff --git a/src/link/MachO/dyld_info/bind.zig b/src/link/MachO/dyld_info/bind.zig
index 328d6a402c..f6697b6275 100644
--- a/src/link/MachO/dyld_info/bind.zig
+++ b/src/link/MachO/dyld_info/bind.zig
@@ -1,7 +1,7 @@
pub const Entry = struct {
target: MachO.Ref,
offset: u64,
- segment_id: u8,
+ segment_id: u4,
addend: i64,
pub fn lessThan(ctx: *MachO, entry: Entry, other: Entry) bool {
@@ -20,14 +20,12 @@ pub const Bind = struct {
entries: std.ArrayListUnmanaged(Entry) = .empty,
buffer: std.ArrayListUnmanaged(u8) = .empty,
- const Self = @This();
-
- pub fn deinit(self: *Self, gpa: Allocator) void {
- self.entries.deinit(gpa);
- self.buffer.deinit(gpa);
+ pub fn deinit(bind: *Bind, gpa: Allocator) void {
+ bind.entries.deinit(gpa);
+ bind.buffer.deinit(gpa);
}
- pub fn updateSize(self: *Self, macho_file: *MachO) !void {
+ pub fn updateSize(bind: *Bind, macho_file: *MachO) !void {
const tracy = trace(@src());
defer tracy.end();
@@ -56,15 +54,12 @@ pub const Bind = struct {
const addend = rel.addend + rel.getRelocAddend(cpu_arch);
const sym = rel.getTargetSymbol(atom.*, macho_file);
if (sym.isTlvInit(macho_file)) continue;
- const entry = Entry{
+ if (sym.flags.import or (!(sym.flags.@"export" and sym.flags.weak) and sym.flags.interposable)) (try bind.entries.addOne(gpa)).* = .{
.target = rel.getTargetSymbolRef(atom.*, macho_file),
.offset = atom_addr + rel_offset - seg.vmaddr,
.segment_id = seg_id,
.addend = addend,
};
- if (sym.flags.import or (!(sym.flags.@"export" and sym.flags.weak) and sym.flags.interposable)) {
- try self.entries.append(gpa, entry);
- }
}
}
}
@@ -75,15 +70,12 @@ pub const Bind = struct {
for (macho_file.got.symbols.items, 0..) |ref, idx| {
const sym = ref.getSymbol(macho_file).?;
const addr = macho_file.got.getAddress(@intCast(idx), macho_file);
- const entry = Entry{
+ if (sym.flags.import or (sym.flags.@"export" and sym.flags.interposable and !sym.flags.weak)) (try bind.entries.addOne(gpa)).* = .{
.target = ref,
.offset = addr - seg.vmaddr,
.segment_id = seg_id,
.addend = 0,
};
- if (sym.flags.import or (sym.flags.@"export" and sym.flags.interposable and !sym.flags.weak)) {
- try self.entries.append(gpa, entry);
- }
}
}
@@ -94,15 +86,12 @@ pub const Bind = struct {
for (macho_file.stubs.symbols.items, 0..) |ref, idx| {
const sym = ref.getSymbol(macho_file).?;
const addr = sect.addr + idx * @sizeOf(u64);
- const bind_entry = Entry{
+ if (sym.flags.import and sym.flags.weak) (try bind.entries.addOne(gpa)).* = .{
.target = ref,
.offset = addr - seg.vmaddr,
.segment_id = seg_id,
.addend = 0,
};
- if (sym.flags.import and sym.flags.weak) {
- try self.entries.append(gpa, bind_entry);
- }
}
}
@@ -113,49 +102,48 @@ pub const Bind = struct {
for (macho_file.tlv_ptr.symbols.items, 0..) |ref, idx| {
const sym = ref.getSymbol(macho_file).?;
const addr = macho_file.tlv_ptr.getAddress(@intCast(idx), macho_file);
- const entry = Entry{
+ if (sym.flags.import or (sym.flags.@"export" and sym.flags.interposable and !sym.flags.weak)) (try bind.entries.addOne(gpa)).* = .{
.target = ref,
.offset = addr - seg.vmaddr,
.segment_id = seg_id,
.addend = 0,
};
- if (sym.flags.import or (sym.flags.@"export" and sym.flags.interposable and !sym.flags.weak)) {
- try self.entries.append(gpa, entry);
- }
}
}
- try self.finalize(gpa, macho_file);
- macho_file.dyld_info_cmd.bind_size = mem.alignForward(u32, @intCast(self.buffer.items.len), @alignOf(u64));
+ try bind.finalize(gpa, macho_file);
+ macho_file.dyld_info_cmd.bind_size = mem.alignForward(u32, @intCast(bind.buffer.items.len), @alignOf(u64));
}
- fn finalize(self: *Self, gpa: Allocator, ctx: *MachO) !void {
- if (self.entries.items.len == 0) return;
+ fn finalize(bind: *Bind, gpa: Allocator, ctx: *MachO) !void {
+ if (bind.entries.items.len == 0) return;
- const writer = self.buffer.writer(gpa);
+ var aw: std.io.Writer.Allocating = .fromArrayList(gpa, &bind.buffer);
+ const bw = &aw.writer;
+ defer bind.buffer = aw.toArrayList();
log.debug("bind opcodes", .{});
- std.mem.sort(Entry, self.entries.items, ctx, Entry.lessThan);
+ std.mem.sort(Entry, bind.entries.items, ctx, Entry.lessThan);
var start: usize = 0;
var seg_id: ?u8 = null;
- for (self.entries.items, 0..) |entry, i| {
+ for (bind.entries.items, 0..) |entry, i| {
if (seg_id != null and seg_id.? == entry.segment_id) continue;
- try finalizeSegment(self.entries.items[start..i], ctx, writer);
+ try finalizeSegment(bind.entries.items[start..i], ctx, bw);
seg_id = entry.segment_id;
start = i;
}
- try finalizeSegment(self.entries.items[start..], ctx, writer);
- try done(writer);
+ try finalizeSegment(bind.entries.items[start..], ctx, bw);
+ try done(bw);
}
- fn finalizeSegment(entries: []const Entry, ctx: *MachO, writer: anytype) !void {
+ fn finalizeSegment(entries: []const Entry, ctx: *MachO, bw: *Writer) Writer.Error!void {
if (entries.len == 0) return;
const seg_id = entries[0].segment_id;
- try setSegmentOffset(seg_id, 0, writer);
+ try setSegmentOffset(seg_id, 0, bw);
var offset: u64 = 0;
var addend: i64 = 0;
@@ -175,15 +163,15 @@ pub const Bind = struct {
if (target == null or !target.?.eql(current.target)) {
switch (state) {
.start => {},
- .bind_single => try doBind(writer),
- .bind_times_skip => try doBindTimesSkip(count, skip, writer),
+ .bind_single => try doBind(bw),
+ .bind_times_skip => try doBindTimesSkip(count, skip, bw),
}
state = .start;
target = current.target;
const sym = current.target.getSymbol(ctx).?;
const name = sym.getName(ctx);
- const flags: u8 = if (sym.weakRef(ctx)) macho.BIND_SYMBOL_FLAGS_WEAK_IMPORT else 0;
+ const flags: u4 = if (sym.weakRef(ctx)) macho.BIND_SYMBOL_FLAGS_WEAK_IMPORT else 0;
const ordinal: i16 = ord: {
if (sym.flags.interposable) break :ord macho.BIND_SPECIAL_DYLIB_FLAT_LOOKUP;
if (sym.flags.import) {
@@ -195,13 +183,13 @@ pub const Bind = struct {
break :ord macho.BIND_SPECIAL_DYLIB_SELF;
};
- try setSymbol(name, flags, writer);
- try setTypePointer(writer);
- try setDylibOrdinal(ordinal, writer);
+ try setSymbol(name, flags, bw);
+ try setTypePointer(bw);
+ try setDylibOrdinal(ordinal, bw);
if (current.addend != addend) {
addend = current.addend;
- try setAddend(addend, writer);
+ try setAddend(addend, bw);
}
}
@@ -210,11 +198,11 @@ pub const Bind = struct {
switch (state) {
.start => {
if (current.offset < offset) {
- try addAddr(@bitCast(@as(i64, @intCast(current.offset)) - @as(i64, @intCast(offset))), writer);
+ try addAddr(@bitCast(@as(i64, @intCast(current.offset)) - @as(i64, @intCast(offset))), bw);
offset = offset - (offset - current.offset);
} else if (current.offset > offset) {
const delta = current.offset - offset;
- try addAddr(delta, writer);
+ try addAddr(delta, bw);
offset += delta;
}
state = .bind_single;
@@ -223,7 +211,7 @@ pub const Bind = struct {
},
.bind_single => {
if (current.offset == offset) {
- try doBind(writer);
+ try doBind(bw);
state = .start;
} else if (current.offset > offset) {
const delta = current.offset - offset;
@@ -237,9 +225,9 @@ pub const Bind = struct {
if (current.offset < offset) {
count -= 1;
if (count == 1) {
- try doBindAddAddr(skip, writer);
+ try doBindAddAddr(skip, bw);
} else {
- try doBindTimesSkip(count, skip, writer);
+ try doBindTimesSkip(count, skip, bw);
}
state = .start;
offset = offset - (@sizeOf(u64) + skip);
@@ -248,7 +236,7 @@ pub const Bind = struct {
count += 1;
offset += @sizeOf(u64) + skip;
} else {
- try doBindTimesSkip(count, skip, writer);
+ try doBindTimesSkip(count, skip, bw);
state = .start;
i -= 1;
}
@@ -258,13 +246,13 @@ pub const Bind = struct {
switch (state) {
.start => unreachable,
- .bind_single => try doBind(writer),
- .bind_times_skip => try doBindTimesSkip(count, skip, writer),
+ .bind_single => try doBind(bw),
+ .bind_times_skip => try doBindTimesSkip(count, skip, bw),
}
}
- pub fn write(self: Self, writer: anytype) !void {
- try writer.writeAll(self.buffer.items);
+ pub fn write(bind: Bind, bw: *Writer) Writer.Error!void {
+ try bw.writeAll(bind.buffer.items);
}
};
@@ -272,14 +260,12 @@ pub const WeakBind = struct {
entries: std.ArrayListUnmanaged(Entry) = .empty,
buffer: std.ArrayListUnmanaged(u8) = .empty,
- const Self = @This();
-
- pub fn deinit(self: *Self, gpa: Allocator) void {
- self.entries.deinit(gpa);
- self.buffer.deinit(gpa);
+ pub fn deinit(bind: *WeakBind, gpa: Allocator) void {
+ bind.entries.deinit(gpa);
+ bind.buffer.deinit(gpa);
}
- pub fn updateSize(self: *Self, macho_file: *MachO) !void {
+ pub fn updateSize(bind: *WeakBind, macho_file: *MachO) !void {
const tracy = trace(@src());
defer tracy.end();
@@ -308,15 +294,12 @@ pub const WeakBind = struct {
const addend = rel.addend + rel.getRelocAddend(cpu_arch);
const sym = rel.getTargetSymbol(atom.*, macho_file);
if (sym.isTlvInit(macho_file)) continue;
- const entry = Entry{
+ if (!sym.isLocal() and sym.flags.weak) (try bind.entries.addOne(gpa)).* = .{
.target = rel.getTargetSymbolRef(atom.*, macho_file),
.offset = atom_addr + rel_offset - seg.vmaddr,
.segment_id = seg_id,
.addend = addend,
};
- if (!sym.isLocal() and sym.flags.weak) {
- try self.entries.append(gpa, entry);
- }
}
}
}
@@ -327,15 +310,12 @@ pub const WeakBind = struct {
for (macho_file.got.symbols.items, 0..) |ref, idx| {
const sym = ref.getSymbol(macho_file).?;
const addr = macho_file.got.getAddress(@intCast(idx), macho_file);
- const entry = Entry{
+ if (sym.flags.weak) (try bind.entries.addOne(gpa)).* = .{
.target = ref,
.offset = addr - seg.vmaddr,
.segment_id = seg_id,
.addend = 0,
};
- if (sym.flags.weak) {
- try self.entries.append(gpa, entry);
- }
}
}
@@ -347,15 +327,12 @@ pub const WeakBind = struct {
for (macho_file.stubs.symbols.items, 0..) |ref, idx| {
const sym = ref.getSymbol(macho_file).?;
const addr = sect.addr + idx * @sizeOf(u64);
- const bind_entry = Entry{
+ if (sym.flags.weak) (try bind.entries.addOne(gpa)).* = .{
.target = ref,
.offset = addr - seg.vmaddr,
.segment_id = seg_id,
.addend = 0,
};
- if (sym.flags.weak) {
- try self.entries.append(gpa, bind_entry);
- }
}
}
@@ -366,49 +343,48 @@ pub const WeakBind = struct {
for (macho_file.tlv_ptr.symbols.items, 0..) |ref, idx| {
const sym = ref.getSymbol(macho_file).?;
const addr = macho_file.tlv_ptr.getAddress(@intCast(idx), macho_file);
- const entry = Entry{
+ if (sym.flags.weak) (try bind.entries.addOne(gpa)).* = .{
.target = ref,
.offset = addr - seg.vmaddr,
.segment_id = seg_id,
.addend = 0,
};
- if (sym.flags.weak) {
- try self.entries.append(gpa, entry);
- }
}
}
- try self.finalize(gpa, macho_file);
- macho_file.dyld_info_cmd.weak_bind_size = mem.alignForward(u32, @intCast(self.buffer.items.len), @alignOf(u64));
+ try bind.finalize(gpa, macho_file);
+ macho_file.dyld_info_cmd.weak_bind_size = mem.alignForward(u32, @intCast(bind.buffer.items.len), @alignOf(u64));
}
- fn finalize(self: *Self, gpa: Allocator, ctx: *MachO) !void {
- if (self.entries.items.len == 0) return;
+ fn finalize(bind: *WeakBind, gpa: Allocator, ctx: *MachO) !void {
+ if (bind.entries.items.len == 0) return;
- const writer = self.buffer.writer(gpa);
+ var aw: std.io.Writer.Allocating = .fromArrayList(gpa, &bind.buffer);
+ const bw = &aw.writer;
+ defer bind.buffer = aw.toArrayList();
log.debug("weak bind opcodes", .{});
- std.mem.sort(Entry, self.entries.items, ctx, Entry.lessThan);
+ std.mem.sort(Entry, bind.entries.items, ctx, Entry.lessThan);
var start: usize = 0;
var seg_id: ?u8 = null;
- for (self.entries.items, 0..) |entry, i| {
+ for (bind.entries.items, 0..) |entry, i| {
if (seg_id != null and seg_id.? == entry.segment_id) continue;
- try finalizeSegment(self.entries.items[start..i], ctx, writer);
+ try finalizeSegment(bind.entries.items[start..i], ctx, bw);
seg_id = entry.segment_id;
start = i;
}
- try finalizeSegment(self.entries.items[start..], ctx, writer);
- try done(writer);
+ try finalizeSegment(bind.entries.items[start..], ctx, bw);
+ try done(bw);
}
- fn finalizeSegment(entries: []const Entry, ctx: *MachO, writer: anytype) !void {
+ fn finalizeSegment(entries: []const Entry, ctx: *MachO, bw: *Writer) Writer.Error!void {
if (entries.len == 0) return;
const seg_id = entries[0].segment_id;
- try setSegmentOffset(seg_id, 0, writer);
+ try setSegmentOffset(seg_id, 0, bw);
var offset: u64 = 0;
var addend: i64 = 0;
@@ -428,8 +404,8 @@ pub const WeakBind = struct {
if (target == null or !target.?.eql(current.target)) {
switch (state) {
.start => {},
- .bind_single => try doBind(writer),
- .bind_times_skip => try doBindTimesSkip(count, skip, writer),
+ .bind_single => try doBind(bw),
+ .bind_times_skip => try doBindTimesSkip(count, skip, bw),
}
state = .start;
target = current.target;
@@ -438,12 +414,12 @@ pub const WeakBind = struct {
const name = sym.getName(ctx);
const flags: u8 = 0; // TODO NON_WEAK_DEFINITION
- try setSymbol(name, flags, writer);
- try setTypePointer(writer);
+ try setSymbol(name, flags, bw);
+ try setTypePointer(bw);
if (current.addend != addend) {
addend = current.addend;
- try setAddend(addend, writer);
+ try setAddend(addend, bw);
}
}
@@ -452,11 +428,11 @@ pub const WeakBind = struct {
switch (state) {
.start => {
if (current.offset < offset) {
- try addAddr(@as(u64, @bitCast(@as(i64, @intCast(current.offset)) - @as(i64, @intCast(offset)))), writer);
+ try addAddr(@as(u64, @bitCast(@as(i64, @intCast(current.offset)) - @as(i64, @intCast(offset)))), bw);
offset = offset - (offset - current.offset);
} else if (current.offset > offset) {
const delta = current.offset - offset;
- try addAddr(delta, writer);
+ try addAddr(delta, bw);
offset += delta;
}
state = .bind_single;
@@ -465,7 +441,7 @@ pub const WeakBind = struct {
},
.bind_single => {
if (current.offset == offset) {
- try doBind(writer);
+ try doBind(bw);
state = .start;
} else if (current.offset > offset) {
const delta = current.offset - offset;
@@ -479,9 +455,9 @@ pub const WeakBind = struct {
if (current.offset < offset) {
count -= 1;
if (count == 1) {
- try doBindAddAddr(skip, writer);
+ try doBindAddAddr(skip, bw);
} else {
- try doBindTimesSkip(count, skip, writer);
+ try doBindTimesSkip(count, skip, bw);
}
state = .start;
offset = offset - (@sizeOf(u64) + skip);
@@ -490,7 +466,7 @@ pub const WeakBind = struct {
count += 1;
offset += @sizeOf(u64) + skip;
} else {
- try doBindTimesSkip(count, skip, writer);
+ try doBindTimesSkip(count, skip, bw);
state = .start;
i -= 1;
}
@@ -500,13 +476,13 @@ pub const WeakBind = struct {
switch (state) {
.start => unreachable,
- .bind_single => try doBind(writer),
- .bind_times_skip => try doBindTimesSkip(count, skip, writer),
+ .bind_single => try doBind(bw),
+ .bind_times_skip => try doBindTimesSkip(count, skip, bw),
}
}
- pub fn write(self: Self, writer: anytype) !void {
- try writer.writeAll(self.buffer.items);
+ pub fn write(bind: WeakBind, bw: *Writer) Writer.Error!void {
+ try bw.writeAll(bind.buffer.items);
}
};
@@ -515,15 +491,13 @@ pub const LazyBind = struct {
buffer: std.ArrayListUnmanaged(u8) = .empty,
offsets: std.ArrayListUnmanaged(u32) = .empty,
- const Self = @This();
-
- pub fn deinit(self: *Self, gpa: Allocator) void {
- self.entries.deinit(gpa);
- self.buffer.deinit(gpa);
- self.offsets.deinit(gpa);
+ pub fn deinit(bind: *LazyBind, gpa: Allocator) void {
+ bind.entries.deinit(gpa);
+ bind.buffer.deinit(gpa);
+ bind.offsets.deinit(gpa);
}
- pub fn updateSize(self: *Self, macho_file: *MachO) !void {
+ pub fn updateSize(bind: *LazyBind, macho_file: *MachO) !void {
const tracy = trace(@src());
defer tracy.end();
@@ -537,36 +511,35 @@ pub const LazyBind = struct {
for (macho_file.stubs.symbols.items, 0..) |ref, idx| {
const sym = ref.getSymbol(macho_file).?;
const addr = sect.addr + idx * @sizeOf(u64);
- const bind_entry = Entry{
+ if ((sym.flags.import and !sym.flags.weak) or (sym.flags.interposable and !sym.flags.weak)) (try bind.entries.addOne(gpa)).* = .{
.target = ref,
.offset = addr - seg.vmaddr,
.segment_id = seg_id,
.addend = 0,
};
- if ((sym.flags.import and !sym.flags.weak) or (sym.flags.interposable and !sym.flags.weak)) {
- try self.entries.append(gpa, bind_entry);
- }
}
- try self.finalize(gpa, macho_file);
- macho_file.dyld_info_cmd.lazy_bind_size = mem.alignForward(u32, @intCast(self.buffer.items.len), @alignOf(u64));
+ try bind.finalize(gpa, macho_file);
+ macho_file.dyld_info_cmd.lazy_bind_size = mem.alignForward(u32, @intCast(bind.buffer.items.len), @alignOf(u64));
}
- fn finalize(self: *Self, gpa: Allocator, ctx: *MachO) !void {
- try self.offsets.ensureTotalCapacityPrecise(gpa, self.entries.items.len);
+ fn finalize(bind: *LazyBind, gpa: Allocator, ctx: *MachO) !void {
+ try bind.offsets.ensureTotalCapacityPrecise(gpa, bind.entries.items.len);
- const writer = self.buffer.writer(gpa);
+ var aw: std.io.Writer.Allocating = .fromArrayList(gpa, &bind.buffer);
+ const bw = &aw.writer;
+ defer bind.buffer = aw.toArrayList();
log.debug("lazy bind opcodes", .{});
var addend: i64 = 0;
- for (self.entries.items) |entry| {
- self.offsets.appendAssumeCapacity(@intCast(self.buffer.items.len));
+ for (bind.entries.items) |entry| {
+ bind.offsets.appendAssumeCapacity(@intCast(bind.buffer.items.len));
const sym = entry.target.getSymbol(ctx).?;
const name = sym.getName(ctx);
- const flags: u8 = if (sym.weakRef(ctx)) macho.BIND_SYMBOL_FLAGS_WEAK_IMPORT else 0;
+ const flags: u4 = if (sym.weakRef(ctx)) macho.BIND_SYMBOL_FLAGS_WEAK_IMPORT else 0;
const ordinal: i16 = ord: {
if (sym.flags.interposable) break :ord macho.BIND_SPECIAL_DYLIB_FLAT_LOOKUP;
if (sym.flags.import) {
@@ -578,121 +551,116 @@ pub const LazyBind = struct {
break :ord macho.BIND_SPECIAL_DYLIB_SELF;
};
- try setSegmentOffset(entry.segment_id, entry.offset, writer);
- try setSymbol(name, flags, writer);
- try setDylibOrdinal(ordinal, writer);
+ try setSegmentOffset(entry.segment_id, entry.offset, bw);
+ try setSymbol(name, flags, bw);
+ try setDylibOrdinal(ordinal, bw);
if (entry.addend != addend) {
- try setAddend(entry.addend, writer);
+ try setAddend(entry.addend, bw);
addend = entry.addend;
}
- try doBind(writer);
- try done(writer);
+ try doBind(bw);
+ try done(bw);
}
}
- pub fn write(self: Self, writer: anytype) !void {
- try writer.writeAll(self.buffer.items);
+ pub fn write(bind: LazyBind, bw: *Writer) Writer.Error!void {
+ try bw.writeAll(bind.buffer.items);
}
};
-fn setSegmentOffset(segment_id: u8, offset: u64, writer: anytype) !void {
+fn setSegmentOffset(segment_id: u4, offset: u64, bw: *Writer) Writer.Error!void {
log.debug(">>> set segment: {d} and offset: {x}", .{ segment_id, offset });
- try writer.writeByte(macho.BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB | @as(u4, @truncate(segment_id)));
- try std.leb.writeUleb128(writer, offset);
+ try bw.writeByte(macho.BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB | segment_id);
+ try bw.writeLeb128(offset);
}
-fn setSymbol(name: []const u8, flags: u8, writer: anytype) !void {
+fn setSymbol(name: []const u8, flags: u4, bw: *Writer) Writer.Error!void {
log.debug(">>> set symbol: {s} with flags: {x}", .{ name, flags });
- try writer.writeByte(macho.BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM | @as(u4, @truncate(flags)));
- try writer.writeAll(name);
- try writer.writeByte(0);
+ try bw.writeByte(macho.BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM | flags);
+ try bw.writeAll(name);
+ try bw.writeByte(0);
}
-fn setTypePointer(writer: anytype) !void {
+fn setTypePointer(bw: *Writer) Writer.Error!void {
log.debug(">>> set type: {d}", .{macho.BIND_TYPE_POINTER});
- try writer.writeByte(macho.BIND_OPCODE_SET_TYPE_IMM | @as(u4, @truncate(macho.BIND_TYPE_POINTER)));
+ try bw.writeByte(macho.BIND_OPCODE_SET_TYPE_IMM | @as(u4, @intCast(macho.BIND_TYPE_POINTER)));
}
-fn setDylibOrdinal(ordinal: i16, writer: anytype) !void {
- if (ordinal <= 0) {
- switch (ordinal) {
- macho.BIND_SPECIAL_DYLIB_SELF,
- macho.BIND_SPECIAL_DYLIB_MAIN_EXECUTABLE,
- macho.BIND_SPECIAL_DYLIB_FLAT_LOOKUP,
- => {},
- else => unreachable, // Invalid dylib special binding
- }
- log.debug(">>> set dylib special: {d}", .{ordinal});
- const cast = @as(u16, @bitCast(ordinal));
- try writer.writeByte(macho.BIND_OPCODE_SET_DYLIB_SPECIAL_IMM | @as(u4, @truncate(cast)));
- } else {
- const cast = @as(u16, @bitCast(ordinal));
- log.debug(">>> set dylib ordinal: {d}", .{ordinal});
- if (cast <= 0xf) {
- try writer.writeByte(macho.BIND_OPCODE_SET_DYLIB_ORDINAL_IMM | @as(u4, @truncate(cast)));
- } else {
- try writer.writeByte(macho.BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB);
- try std.leb.writeUleb128(writer, cast);
- }
+fn setDylibOrdinal(ordinal: i16, bw: *Writer) Writer.Error!void {
+ switch (ordinal) {
+ else => unreachable, // Invalid dylib special binding
+ macho.BIND_SPECIAL_DYLIB_SELF,
+ macho.BIND_SPECIAL_DYLIB_MAIN_EXECUTABLE,
+ macho.BIND_SPECIAL_DYLIB_FLAT_LOOKUP,
+ => {
+ log.debug(">>> set dylib special: {d}", .{ordinal});
+ try bw.writeByte(macho.BIND_OPCODE_SET_DYLIB_SPECIAL_IMM | @as(u4, @bitCast(@as(i4, @intCast(ordinal)))));
+ },
+ 1...std.math.maxInt(i16) => {
+ log.debug(">>> set dylib ordinal: {d}", .{ordinal});
+ if (std.math.cast(u4, ordinal)) |imm| {
+ try bw.writeByte(macho.BIND_OPCODE_SET_DYLIB_ORDINAL_IMM | imm);
+ } else {
+ try bw.writeByte(macho.BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB);
+ try bw.writeUleb128(ordinal);
+ }
+ },
}
}
-fn setAddend(addend: i64, writer: anytype) !void {
+fn setAddend(addend: i64, bw: *Writer) Writer.Error!void {
log.debug(">>> set addend: {x}", .{addend});
- try writer.writeByte(macho.BIND_OPCODE_SET_ADDEND_SLEB);
- try std.leb.writeIleb128(writer, addend);
+ try bw.writeByte(macho.BIND_OPCODE_SET_ADDEND_SLEB);
+ try bw.writeLeb128(addend);
}
-fn doBind(writer: anytype) !void {
+fn doBind(bw: *Writer) Writer.Error!void {
log.debug(">>> bind", .{});
- try writer.writeByte(macho.BIND_OPCODE_DO_BIND);
+ try bw.writeByte(macho.BIND_OPCODE_DO_BIND);
}
-fn doBindAddAddr(addr: u64, writer: anytype) !void {
+fn doBindAddAddr(addr: u64, bw: *Writer) Writer.Error!void {
log.debug(">>> bind with add: {x}", .{addr});
- if (std.mem.isAlignedGeneric(u64, addr, @sizeOf(u64))) {
- const imm = @divExact(addr, @sizeOf(u64));
- if (imm <= 0xf) {
- try writer.writeByte(
- macho.BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED | @as(u4, @truncate(imm)),
- );
- return;
- }
- }
- try writer.writeByte(macho.BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB);
- try std.leb.writeUleb128(writer, addr);
+ if (std.math.divExact(u64, addr, @sizeOf(u64))) |scaled| {
+ if (std.math.cast(u4, scaled)) |imm_scaled| return bw.writeByte(
+ macho.BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED | imm_scaled,
+ );
+ } else |_| {}
+ try bw.writeByte(macho.BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB);
+ try bw.writeLeb128(addr);
}
-fn doBindTimesSkip(count: usize, skip: u64, writer: anytype) !void {
+fn doBindTimesSkip(count: usize, skip: u64, bw: *Writer) Writer.Error!void {
log.debug(">>> bind with count: {d} and skip: {x}", .{ count, skip });
- try writer.writeByte(macho.BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB);
- try std.leb.writeUleb128(writer, count);
- try std.leb.writeUleb128(writer, skip);
+ try bw.writeByte(macho.BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB);
+ try bw.writeLeb128(count);
+ try bw.writeLeb128(skip);
}
-fn addAddr(addr: u64, writer: anytype) !void {
+fn addAddr(addr: u64, bw: *Writer) Writer.Error!void {
log.debug(">>> add: {x}", .{addr});
- try writer.writeByte(macho.BIND_OPCODE_ADD_ADDR_ULEB);
- try std.leb.writeUleb128(writer, addr);
+ try bw.writeByte(macho.BIND_OPCODE_ADD_ADDR_ULEB);
+ try bw.writeLeb128(addr);
}
-fn done(writer: anytype) !void {
+fn done(bw: *Writer) Writer.Error!void {
log.debug(">>> done", .{});
- try writer.writeByte(macho.BIND_OPCODE_DONE);
+ try bw.writeByte(macho.BIND_OPCODE_DONE);
}
+const std = @import("std");
const assert = std.debug.assert;
const leb = std.leb;
const log = std.log.scoped(.link_dyld_info);
const macho = std.macho;
const mem = std.mem;
const testing = std.testing;
-const trace = @import("../../../tracy.zig").trace;
-const std = @import("std");
+const Allocator = std.mem.Allocator;
+const Writer = std.io.Writer;
-const Allocator = mem.Allocator;
+const trace = @import("../../../tracy.zig").trace;
const File = @import("../file.zig").File;
const MachO = @import("../../MachO.zig");
const Symbol = @import("../Symbol.zig");