1const std = @import("std");
2const leb = std.leb;
3const macho = std.macho;
4
5pub const Pointer = struct {
6    offset: u64,
7    segment_id: u16,
8    dylib_ordinal: ?i64 = null,
9    name: ?[]const u8 = null,
10};
11
12pub fn rebaseInfoSize(pointers: []const Pointer) !u64 {
13    var stream = std.io.countingWriter(std.io.null_writer);
14    var writer = stream.writer();
15    var size: u64 = 0;
16
17    for (pointers) |pointer| {
18        size += 2;
19        try leb.writeILEB128(writer, pointer.offset);
20        size += 1;
21    }
22
23    size += 1 + stream.bytes_written;
24    return size;
25}
26
27pub fn writeRebaseInfo(pointers: []const Pointer, writer: anytype) !void {
28    for (pointers) |pointer| {
29        try writer.writeByte(macho.REBASE_OPCODE_SET_TYPE_IMM | @truncate(u4, macho.REBASE_TYPE_POINTER));
30        try writer.writeByte(macho.REBASE_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB | @truncate(u4, pointer.segment_id));
31
32        try leb.writeILEB128(writer, pointer.offset);
33        try writer.writeByte(macho.REBASE_OPCODE_DO_REBASE_IMM_TIMES | @truncate(u4, 1));
34    }
35    try writer.writeByte(macho.REBASE_OPCODE_DONE);
36}
37
38pub fn bindInfoSize(pointers: []const Pointer) !u64 {
39    var stream = std.io.countingWriter(std.io.null_writer);
40    var writer = stream.writer();
41    var size: u64 = 0;
42
43    for (pointers) |pointer| {
44        size += 1;
45        if (pointer.dylib_ordinal.? > 15) {
46            try leb.writeULEB128(writer, @bitCast(u64, pointer.dylib_ordinal.?));
47        }
48        size += 1;
49
50        size += 1;
51        size += pointer.name.?.len;
52        size += 1;
53
54        size += 1;
55
56        try leb.writeILEB128(writer, pointer.offset);
57        size += 1;
58    }
59
60    size += stream.bytes_written + 1;
61    return size;
62}
63
64pub fn writeBindInfo(pointers: []const Pointer, writer: anytype) !void {
65    for (pointers) |pointer| {
66        if (pointer.dylib_ordinal.? > 15) {
67            try writer.writeByte(macho.BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB);
68            try leb.writeULEB128(writer, @bitCast(u64, pointer.dylib_ordinal.?));
69        } else if (pointer.dylib_ordinal.? > 0) {
70            try writer.writeByte(macho.BIND_OPCODE_SET_DYLIB_ORDINAL_IMM | @truncate(u4, @bitCast(u64, pointer.dylib_ordinal.?)));
71        } else {
72            try writer.writeByte(macho.BIND_OPCODE_SET_DYLIB_SPECIAL_IMM | @truncate(u4, @bitCast(u64, pointer.dylib_ordinal.?)));
73        }
74        try writer.writeByte(macho.BIND_OPCODE_SET_TYPE_IMM | @truncate(u4, macho.BIND_TYPE_POINTER));
75
76        try writer.writeByte(macho.BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM); // TODO Sometimes we might want to add flags.
77        try writer.writeAll(pointer.name.?);
78        try writer.writeByte(0);
79
80        try writer.writeByte(macho.BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB | @truncate(u4, pointer.segment_id));
81
82        try leb.writeILEB128(writer, pointer.offset);
83        try writer.writeByte(macho.BIND_OPCODE_DO_BIND);
84    }
85
86    try writer.writeByte(macho.BIND_OPCODE_DONE);
87}
88
89pub fn lazyBindInfoSize(pointers: []const Pointer) !u64 {
90    var stream = std.io.countingWriter(std.io.null_writer);
91    var writer = stream.writer();
92    var size: u64 = 0;
93
94    for (pointers) |pointer| {
95        size += 1;
96
97        try leb.writeILEB128(writer, pointer.offset);
98
99        size += 1;
100        if (pointer.dylib_ordinal.? > 15) {
101            try leb.writeULEB128(writer, @bitCast(u64, pointer.dylib_ordinal.?));
102        }
103
104        size += 1;
105        size += pointer.name.?.len;
106        size += 1;
107
108        size += 2;
109    }
110
111    size += stream.bytes_written;
112    return size;
113}
114
115pub fn writeLazyBindInfo(pointers: []const Pointer, writer: anytype) !void {
116    for (pointers) |pointer| {
117        try writer.writeByte(macho.BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB | @truncate(u4, pointer.segment_id));
118
119        try leb.writeILEB128(writer, pointer.offset);
120
121        if (pointer.dylib_ordinal.? > 15) {
122            try writer.writeByte(macho.BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB);
123            try leb.writeULEB128(writer, @bitCast(u64, pointer.dylib_ordinal.?));
124        } else if (pointer.dylib_ordinal.? > 0) {
125            try writer.writeByte(macho.BIND_OPCODE_SET_DYLIB_ORDINAL_IMM | @truncate(u4, @bitCast(u64, pointer.dylib_ordinal.?)));
126        } else {
127            try writer.writeByte(macho.BIND_OPCODE_SET_DYLIB_SPECIAL_IMM | @truncate(u4, @bitCast(u64, pointer.dylib_ordinal.?)));
128        }
129
130        try writer.writeByte(macho.BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM); // TODO Sometimes we might want to add flags.
131        try writer.writeAll(pointer.name.?);
132        try writer.writeByte(0);
133
134        try writer.writeByte(macho.BIND_OPCODE_DO_BIND);
135        try writer.writeByte(macho.BIND_OPCODE_DONE);
136    }
137}
138