authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2020-12-13 21:26:57+01:00
committergravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2020-12-17 10:04:53+01:00
logae535111a40ad0a5fe87304e171fd093a575494e
treeefa2d1c03dbf676e9bc8f61121b86503ae63dfc0
parent5e913c9c2c41e44620911ffce0ebc20d9af041ca

macho: cleanup (lazy) binding info tables


3 files changed, 267 insertions(+), 93 deletions(-)

src/link/MachO.zig+112-35
...@@ -810,47 +810,50 @@ fn linkWithLLD(self: *MachO, comp: *Compilation) !void {...@@ -810,47 +810,50 @@ fn linkWithLLD(self: *MachO, comp: *Compilation) !void {
810 if (self.symtab_cmd_index == null or self.dysymtab_cmd_index == null) {810 if (self.symtab_cmd_index == null or self.dysymtab_cmd_index == null) {
811 std.log.err("Incomplete Mach-O binary: no LC_SYMTAB or LC_DYSYMTAB load command found!", .{});811 std.log.err("Incomplete Mach-O binary: no LC_SYMTAB or LC_DYSYMTAB load command found!", .{});
812 std.log.err("Without the symbol table, it is not possible to patch up the binary for cross-compilation.", .{});812 std.log.err("Without the symbol table, it is not possible to patch up the binary for cross-compilation.", .{});
813 return error.NoSymbolTable;813 return error.NoSymbolTableFound;
814 }814 }
815815
816 // Parse symbol and string tables.816 // Parse symbol and string tables.
817 try self.parseSymbolTable();817 try self.parseSymbolTable();
818 try self.parseStringTable();818 try self.parseStringTable();
819819
820 std.debug.print("Undef symbols\n", .{});820 // Parse dyld info
821 for (self.undef_symbols.items) |sym| {821 try self.parseBindingInfoTable();
822 const name = self.string_table.items[sym.n_strx..];822 try self.parseLazyBindingInfoTable();
823 const len = blk: {823
824 var end: usize = 0;824 // Update the dylib ordinals.
825 while (true) {825 self.binding_info_table.dylib_ordinal = next_ordinal;
826 if (name[end] == @as(u8, 0)) break;826 for (self.lazy_binding_info_table.symbols.items) |*symbol| {
827 end += 1;827 symbol.dylib_ordinal = next_ordinal;
828 }
829 break :blk end;
830 };
831 std.debug.print("name={},sym={}\n", .{ name[0..len], sym });
832 }828 }
833829
834 // Parse dyld info830 // Write update dyld info
835 var symbols_by_name = std.StringHashMap(u16).init(self.base.allocator);831 const dyld_info = self.load_commands.items[self.dyld_info_cmd_index.?].DyldInfoOnly;
836 defer symbols_by_name.deinit();832 {
837 try symbols_by_name.ensureCapacity(@intCast(u32, self.undef_symbols.items.len));833 const size = self.binding_info_table.calcSize();
838834 assert(dyld_info.bind_size == size);
839 for (self.undef_symbols.items) |sym, i| {835
840 const name = self.string_table.items[sym.n_strx..];836 var buffer = try self.base.allocator.alloc(u8, size);
841 const len = blk: {837 defer self.base.allocator.free(buffer);
842 var end: usize = 0;838
843 while (true) {839 var stream = std.io.fixedBufferStream(buffer);
844 if (name[end] == @as(u8, 0)) break;840 try self.binding_info_table.write(stream.writer());
845 end += 1;841
846 }842 try self.base.file.?.pwriteAll(buffer, dyld_info.bind_off);
847 break :blk end;843 }
848 };844 {
849 symbols_by_name.putAssumeCapacityNoClobber(name[0..len], @intCast(u16, i));845 const size = self.lazy_binding_info_table.calcSize();
846 assert(dyld_info.lazy_bind_size == size);
847
848 var buffer = try self.base.allocator.alloc(u8, size);
849 defer self.base.allocator.free(buffer);
850
851 var stream = std.io.fixedBufferStream(buffer);
852 try self.lazy_binding_info_table.write(stream.writer());
853
854 try self.base.file.?.pwriteAll(buffer, dyld_info.lazy_bind_off);
850 }855 }
851856
852 try self.parseBindingInfoTable(symbols_by_name);
853 try self.parseLazyBindingInfoTable(symbols_by_name);
854 // Write updated load commands and the header857 // Write updated load commands and the header
855 try self.writeLoadCommands();858 try self.writeLoadCommands();
856 try self.writeHeader();859 try self.writeHeader();
...@@ -1952,6 +1955,68 @@ fn writeExportTrie(self: *MachO) !void {...@@ -1952,6 +1955,68 @@ fn writeExportTrie(self: *MachO) !void {
1952 self.cmd_table_dirty = true;1955 self.cmd_table_dirty = true;
1953}1956}
19541957
1958fn writeBindingInfoTable(self: *MachO) !void {
1959 const size = self.binding_info_table.calcSize();
1960 var buffer = try self.base.allocator.alloc(u8, size);
1961 defer self.base.allocator.free(buffer);
1962
1963 var stream = std.io.fixedBufferStream(buffer);
1964 try self.binding_info_table.write(stream.writer());
1965
1966 const dyld_info = &self.load_commands.items[self.dyld_info_cmd_index.?].DyldInfoOnly;
1967 const bind_size = @intCast(u32, mem.alignForward(buffer.len, @sizeOf(u64)));
1968 dyld_info.bind_off = self.linkedit_segment_next_offset.?;
1969 dyld_info.bind_size = bind_size;
1970
1971 log.debug("writing binding info table from 0x{x} to 0x{x}\n", .{ dyld_info.bind_off, dyld_info.bind_off + bind_size });
1972
1973 if (bind_size > buffer.len) {
1974 // Pad out to align(8).
1975 try self.base.file.?.pwriteAll(&[_]u8{0}, dyld_info.bind_off + bind_size);
1976 }
1977 try self.base.file.?.pwriteAll(buffer, dyld_info.bind_off);
1978
1979 self.linkedit_segment_next_offset = dyld_info.bind_off + dyld_info.bind_size;
1980 // Advance size of __LINKEDIT segment
1981 const linkedit = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment;
1982 linkedit.inner.filesize += dyld_info.bind_size;
1983 if (linkedit.inner.vmsize < linkedit.inner.filesize) {
1984 linkedit.inner.vmsize = mem.alignForwardGeneric(u64, linkedit.inner.filesize, self.page_size);
1985 }
1986 self.cmd_table_dirty = true;
1987}
1988
1989fn writeLazyBindingInfoTable(self: *MachO) !void {
1990 const size = self.lazy_binding_info_table.calcSize();
1991 var buffer = try self.base.allocator.alloc(u8, size);
1992 defer self.base.allocator.free(buffer);
1993
1994 var stream = std.io.fixedBufferStream(buffer);
1995 try self.lazy_binding_info_table.write(stream.writer());
1996
1997 const dyld_info = &self.load_commands.items[self.dyld_info_cmd_index.?].DyldInfoOnly;
1998 const bind_size = @intCast(u32, mem.alignForward(buffer.len, @sizeOf(u64)));
1999 dyld_info.lazy_bind_off = self.linkedit_segment_next_offset.?;
2000 dyld_info.lazy_bind_size = bind_size;
2001
2002 log.debug("writing lazy binding info table from 0x{x} to 0x{x}\n", .{ dyld_info.lazy_bind_off, dyld_info.lazy_bind_off + bind_size });
2003
2004 if (bind_size > buffer.len) {
2005 // Pad out to align(8).
2006 try self.base.file.?.pwriteAll(&[_]u8{0}, dyld_info.lazy_bind_off + bind_size);
2007 }
2008 try self.base.file.?.pwriteAll(buffer, dyld_info.lazy_bind_off);
2009
2010 self.linkedit_segment_next_offset = dyld_info.lazy_bind_off + dyld_info.lazy_bind_size;
2011 // Advance size of __LINKEDIT segment
2012 const linkedit = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment;
2013 linkedit.inner.filesize += dyld_info.lazy_bind_size;
2014 if (linkedit.inner.vmsize < linkedit.inner.filesize) {
2015 linkedit.inner.vmsize = mem.alignForwardGeneric(u64, linkedit.inner.filesize, self.page_size);
2016 }
2017 self.cmd_table_dirty = true;
2018}
2019
1955fn writeStringTable(self: *MachO) !void {2020fn writeStringTable(self: *MachO) !void {
1956 const symtab = &self.load_commands.items[self.symtab_cmd_index.?].Symtab;2021 const symtab = &self.load_commands.items[self.symtab_cmd_index.?].Symtab;
1957 const needed_size = self.string_table.items.len;2022 const needed_size = self.string_table.items.len;
...@@ -2122,7 +2187,7 @@ fn parseStringTable(self: *MachO) !void {...@@ -2122,7 +2187,7 @@ fn parseStringTable(self: *MachO) !void {
2122 self.string_table.appendSliceAssumeCapacity(buffer);2187 self.string_table.appendSliceAssumeCapacity(buffer);
2123}2188}
21242189
2125fn parseBindingInfoTable(self: *MachO, symbols_by_name: std.StringHashMap(u16)) !void {2190fn parseBindingInfoTable(self: *MachO) !void {
2126 const dyld_info = self.load_commands.items[self.dyld_info_cmd_index.?].DyldInfoOnly;2191 const dyld_info = self.load_commands.items[self.dyld_info_cmd_index.?].DyldInfoOnly;
2127 var buffer = try self.base.allocator.alloc(u8, dyld_info.bind_size);2192 var buffer = try self.base.allocator.alloc(u8, dyld_info.bind_size);
2128 defer self.base.allocator.free(buffer);2193 defer self.base.allocator.free(buffer);
...@@ -2130,10 +2195,10 @@ fn parseBindingInfoTable(self: *MachO, symbols_by_name: std.StringHashMap(u16))...@@ -2130,10 +2195,10 @@ fn parseBindingInfoTable(self: *MachO, symbols_by_name: std.StringHashMap(u16))
2130 assert(nread == buffer.len);2195 assert(nread == buffer.len);
21312196
2132 var stream = std.io.fixedBufferStream(buffer);2197 var stream = std.io.fixedBufferStream(buffer);
2133 try self.binding_info_table.read(self.base.allocator, symbols_by_name, stream.reader());2198 try self.binding_info_table.read(stream.reader(), self.base.allocator);
2134}2199}
21352200
2136fn parseLazyBindingInfoTable(self: *MachO, symbols_by_name: std.StringHashMap(u16)) !void {2201fn parseLazyBindingInfoTable(self: *MachO) !void {
2137 const dyld_info = self.load_commands.items[self.dyld_info_cmd_index.?].DyldInfoOnly;2202 const dyld_info = self.load_commands.items[self.dyld_info_cmd_index.?].DyldInfoOnly;
2138 var buffer = try self.base.allocator.alloc(u8, dyld_info.lazy_bind_size);2203 var buffer = try self.base.allocator.alloc(u8, dyld_info.lazy_bind_size);
2139 defer self.base.allocator.free(buffer);2204 defer self.base.allocator.free(buffer);
...@@ -2141,5 +2206,17 @@ fn parseLazyBindingInfoTable(self: *MachO, symbols_by_name: std.StringHashMap(u1...@@ -2141,5 +2206,17 @@ fn parseLazyBindingInfoTable(self: *MachO, symbols_by_name: std.StringHashMap(u1
2141 assert(nread == buffer.len);2206 assert(nread == buffer.len);
21422207
2143 var stream = std.io.fixedBufferStream(buffer);2208 var stream = std.io.fixedBufferStream(buffer);
2144 try self.lazy_binding_info_table.read(self.base.allocator, symbols_by_name, stream.reader());2209 try self.lazy_binding_info_table.read(stream.reader(), self.base.allocator);
2210}
2211
2212/// Calculates number of bytes in LEB128 encoding of value.
2213pub fn sizeLEB128(value: anytype) usize {
2214 var res: usize = 0;
2215 var v = value;
2216 while (true) {
2217 v = v >> 7;
2218 res += 1;
2219 if (v == 0) break;
2220 }
2221 return res;
2145}2222}
src/link/MachO/Trie.zig+6-19
...@@ -38,6 +38,7 @@ const macho = std.macho;...@@ -38,6 +38,7 @@ const macho = std.macho;
38const testing = std.testing;38const testing = std.testing;
39const assert = std.debug.assert;39const assert = std.debug.assert;
40const Allocator = mem.Allocator;40const Allocator = mem.Allocator;
41const sizeLEB128 = @import("../MachO.zig").sizeLEB128;
4142
42pub const Node = struct {43pub const Node = struct {
43 base: *Trie,44 base: *Trie,
...@@ -244,9 +245,9 @@ pub const Node = struct {...@@ -244,9 +245,9 @@ pub const Node = struct {
244 fn finalize(self: *Node, offset_in_trie: usize) FinalizeResult {245 fn finalize(self: *Node, offset_in_trie: usize) FinalizeResult {
245 var node_size: usize = 0;246 var node_size: usize = 0;
246 if (self.terminal_info) |info| {247 if (self.terminal_info) |info| {
247 node_size += sizeULEB128Mem(info.export_flags);248 node_size += sizeLEB128(info.export_flags);
248 node_size += sizeULEB128Mem(info.vmaddr_offset);249 node_size += sizeLEB128(info.vmaddr_offset);
249 node_size += sizeULEB128Mem(node_size);250 node_size += sizeLEB128(node_size);
250 } else {251 } else {
251 node_size += 1; // 0x0 for non-terminal nodes252 node_size += 1; // 0x0 for non-terminal nodes
252 }253 }
...@@ -254,7 +255,7 @@ pub const Node = struct {...@@ -254,7 +255,7 @@ pub const Node = struct {
254255
255 for (self.edges.items) |edge| {256 for (self.edges.items) |edge| {
256 const next_node_offset = edge.to.trie_offset orelse 0;257 const next_node_offset = edge.to.trie_offset orelse 0;
257 node_size += edge.label.len + 1 + sizeULEB128Mem(next_node_offset);258 node_size += edge.label.len + 1 + sizeLEB128(next_node_offset);
258 }259 }
259260
260 const trie_offset = self.trie_offset orelse 0;261 const trie_offset = self.trie_offset orelse 0;
...@@ -264,18 +265,6 @@ pub const Node = struct {...@@ -264,18 +265,6 @@ pub const Node = struct {
264265
265 return .{ .node_size = node_size, .updated = updated };266 return .{ .node_size = node_size, .updated = updated };
266 }267 }
267
268 /// Calculates number of bytes in ULEB128 encoding of value.
269 fn sizeULEB128Mem(value: u64) usize {
270 var res: usize = 0;
271 var v = value;
272 while (true) {
273 v = v >> 7;
274 res += 1;
275 if (v == 0) break;
276 }
277 return res;
278 }
279};268};
280269
281/// The root node of the trie.270/// The root node of the trie.
...@@ -380,9 +369,7 @@ pub fn read(self: *Trie, reader: anytype) ReadError!usize {...@@ -380,9 +369,7 @@ pub fn read(self: *Trie, reader: anytype) ReadError!usize {
380}369}
381370
382/// Write the trie to a byte stream.371/// Write the trie to a byte stream.
383/// Caller owns the memory and needs to free it.372/// Panics if the trie was not finalized using `finalize` before calling this method.
384/// Panics if the trie was not finalized using `finalize`
385/// before calling this method.
386pub fn write(self: Trie, writer: anytype) !usize {373pub fn write(self: Trie, writer: anytype) !usize {
387 assert(!self.trie_dirty);374 assert(!self.trie_dirty);
388 var counting_writer = std.io.countingWriter(writer);375 var counting_writer = std.io.countingWriter(writer);
src/link/MachO/imports.zig+149-39
...@@ -5,16 +5,22 @@ const mem = std.mem;...@@ -5,16 +5,22 @@ const mem = std.mem;
55
6const assert = std.debug.assert;6const assert = std.debug.assert;
7const Allocator = mem.Allocator;7const Allocator = mem.Allocator;
8const sizeLEB128 = @import("../MachO.zig").sizeLEB128;
89
10/// Table of binding info entries used to tell the dyld which
11/// symbols to bind at loading time.
9pub const BindingInfoTable = struct {12pub const BindingInfoTable = struct {
13 /// Id of the dynamic library where the specified entries can be found.
10 dylib_ordinal: i64 = 0,14 dylib_ordinal: i64 = 0,
15
16 /// Binding type; defaults to pointer type.
11 binding_type: u8 = macho.BIND_TYPE_POINTER,17 binding_type: u8 = macho.BIND_TYPE_POINTER,
12 entries: std.ArrayListUnmanaged(Entry) = .{},
1318
14 pub const Entry = struct {19 symbols: std.ArrayListUnmanaged(Symbol) = .{},
15 /// Id of the symbol in the undef symbol table.20
16 /// Can be null.21 pub const Symbol = struct {
17 symbol: ?u16 = null,22 /// Symbol name.
23 name: ?[]u8 = null,
1824
19 /// Id of the segment where to bind this symbol to.25 /// Id of the segment where to bind this symbol to.
20 segment: u8,26 segment: u8,
...@@ -24,14 +30,17 @@ pub const BindingInfoTable = struct {...@@ -24,14 +30,17 @@ pub const BindingInfoTable = struct {
24 };30 };
2531
26 pub fn deinit(self: *BindingInfoTable, allocator: *Allocator) void {32 pub fn deinit(self: *BindingInfoTable, allocator: *Allocator) void {
27 self.entries.deinit(allocator);33 for (self.symbols.items) |*symbol| {
34 if (symbol.name) |name| {
35 allocator.free(name);
36 }
37 }
38 self.symbols.deinit(allocator);
28 }39 }
2940
30 pub fn read(self: *BindingInfoTable, allocator: *Allocator, symbols_by_name: anytype, reader: anytype) !void {41 /// Parse the binding info table from byte stream.
31 var name = std.ArrayList(u8).init(allocator);42 pub fn read(self: *BindingInfoTable, reader: anytype, allocator: *Allocator) !void {
32 defer name.deinit();43 var symbol: Symbol = .{
33
34 var entry: Entry = .{
35 .segment = 0,44 .segment = 0,
36 .offset = 0,45 .offset = 0,
37 };46 };
...@@ -48,8 +57,8 @@ pub const BindingInfoTable = struct {...@@ -48,8 +57,8 @@ pub const BindingInfoTable = struct {
4857
49 switch (opcode) {58 switch (opcode) {
50 macho.BIND_OPCODE_DO_BIND => {59 macho.BIND_OPCODE_DO_BIND => {
51 try self.entries.append(allocator, entry);60 try self.symbols.append(allocator, symbol);
52 entry = .{61 symbol = .{
53 .segment = 0,62 .segment = 0,
54 .offset = 0,63 .offset = 0,
55 };64 };
...@@ -59,17 +68,17 @@ pub const BindingInfoTable = struct {...@@ -59,17 +68,17 @@ pub const BindingInfoTable = struct {
59 break;68 break;
60 },69 },
61 macho.BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM => {70 macho.BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM => {
62 name.shrinkRetainingCapacity(0);71 var name = std.ArrayList(u8).init(allocator);
63 var next = try reader.readByte();72 var next = try reader.readByte();
64 while (next != @as(u8, 0)) {73 while (next != @as(u8, 0)) {
65 try name.append(next);74 try name.append(next);
66 next = try reader.readByte();75 next = try reader.readByte();
67 }76 }
68 entry.symbol = symbols_by_name.get(name.items[0..]);77 symbol.name = name.toOwnedSlice();
69 },78 },
70 macho.BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB => {79 macho.BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB => {
71 entry.segment = imm;80 symbol.segment = imm;
72 entry.offset = try leb.readILEB128(i64, reader);81 symbol.offset = try leb.readILEB128(i64, reader);
73 },82 },
74 macho.BIND_OPCODE_SET_DYLIB_SPECIAL_IMM, macho.BIND_OPCODE_SET_DYLIB_ORDINAL_IMM => {83 macho.BIND_OPCODE_SET_DYLIB_SPECIAL_IMM, macho.BIND_OPCODE_SET_DYLIB_ORDINAL_IMM => {
75 assert(!dylib_ordinal_set);84 assert(!dylib_ordinal_set);
...@@ -90,15 +99,69 @@ pub const BindingInfoTable = struct {...@@ -90,15 +99,69 @@ pub const BindingInfoTable = struct {
90 assert(done);99 assert(done);
91 }100 }
92101
93 pub fn write(self: BindingInfoTable, writer: anytype) !void {}102 /// Write the binding info table to byte stream.
103 pub fn write(self: BindingInfoTable, writer: anytype) !void {
104 if (self.dylib_ordinal > 15) {
105 try writer.writeByte(macho.BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB);
106 try leb.writeULEB128(writer, @bitCast(u64, self.dylib_ordinal));
107 } else if (self.dylib_ordinal > 0) {
108 try writer.writeByte(macho.BIND_OPCODE_SET_DYLIB_ORDINAL_IMM | @truncate(u4, @bitCast(u64, self.dylib_ordinal)));
109 } else {
110 try writer.writeByte(macho.BIND_OPCODE_SET_DYLIB_SPECIAL_IMM | @truncate(u4, @bitCast(u64, self.dylib_ordinal)));
111 }
112 try writer.writeByte(macho.BIND_OPCODE_SET_TYPE_IMM | @truncate(u4, self.binding_type));
113
114 for (self.symbols.items) |symbol| {
115 if (symbol.name) |name| {
116 try writer.writeByte(macho.BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM); // TODO Sometimes we might want to add flags.
117 try writer.writeAll(name);
118 try writer.writeByte(0);
119 }
120
121 try writer.writeByte(macho.BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB | @truncate(u4, symbol.segment));
122 try leb.writeILEB128(writer, symbol.offset);
123
124 try writer.writeByte(macho.BIND_OPCODE_DO_BIND);
125 }
126
127 try writer.writeByte(macho.BIND_OPCODE_DONE);
128 }
129
130 /// Calculate size in bytes of this binding info table.
131 pub fn calcSize(self: *BindingInfoTable) usize {
132 var size: usize = 1;
133 if (self.dylib_ordinal > 15) {
134 size += sizeLEB128(self.dylib_ordinal);
135 }
136
137 size += 1;
138
139 for (self.symbols.items) |symbol| {
140 if (symbol.name) |name| {
141 size += 1;
142 size += name.len;
143 size += 1;
144 }
145
146 size += 1;
147 size += sizeLEB128(symbol.offset);
148
149 size += 1;
150 }
151
152 size += 1;
153 return size;
154 }
94};155};
95156
157/// Table of lazy binding info entries used to tell the dyld which
158/// symbols to lazily bind at first load of a dylib.
96pub const LazyBindingInfoTable = struct {159pub const LazyBindingInfoTable = struct {
97 entries: std.ArrayListUnmanaged(Entry) = .{},160 symbols: std.ArrayListUnmanaged(Symbol) = .{},
98161
99 pub const Entry = struct {162 pub const Symbol = struct {
100 /// Id of the symbol in the undef symbol table.163 /// Symbol name.
101 symbol: u16,164 name: ?[]u8 = null,
102165
103 /// Offset of this symbol wrt to the segment id encoded in `segment`.166 /// Offset of this symbol wrt to the segment id encoded in `segment`.
104 offset: i64,167 offset: i64,
...@@ -113,15 +176,17 @@ pub const LazyBindingInfoTable = struct {...@@ -113,15 +176,17 @@ pub const LazyBindingInfoTable = struct {
113 };176 };
114177
115 pub fn deinit(self: *LazyBindingInfoTable, allocator: *Allocator) void {178 pub fn deinit(self: *LazyBindingInfoTable, allocator: *Allocator) void {
116 self.entries.deinit(allocator);179 for (self.symbols.items) |*symbol| {
180 if (symbol.name) |name| {
181 allocator.free(name);
182 }
183 }
184 self.symbols.deinit(allocator);
117 }185 }
118186
119 pub fn read(self: *LazyBindingInfoTable, allocator: *Allocator, symbols_by_name: anytype, reader: anytype) !void {187 /// Parse the binding info table from byte stream.
120 var name = std.ArrayList(u8).init(allocator);188 pub fn read(self: *LazyBindingInfoTable, reader: anytype, allocator: *Allocator) !void {
121 defer name.deinit();189 var symbol: Symbol = .{
122
123 var entry: Entry = .{
124 .symbol = 0,
125 .offset = 0,190 .offset = 0,
126 .segment = 0,191 .segment = 0,
127 .dylib_ordinal = 0,192 .dylib_ordinal = 0,
...@@ -138,35 +203,34 @@ pub const LazyBindingInfoTable = struct {...@@ -138,35 +203,34 @@ pub const LazyBindingInfoTable = struct {
138203
139 switch (opcode) {204 switch (opcode) {
140 macho.BIND_OPCODE_DO_BIND => {205 macho.BIND_OPCODE_DO_BIND => {
141 try self.entries.append(allocator, entry);206 try self.symbols.append(allocator, symbol);
142 },207 },
143 macho.BIND_OPCODE_DONE => {208 macho.BIND_OPCODE_DONE => {
144 done = true;209 done = true;
145 entry = .{210 symbol = .{
146 .symbol = 0,
147 .offset = 0,211 .offset = 0,
148 .segment = 0,212 .segment = 0,
149 .dylib_ordinal = 0,213 .dylib_ordinal = 0,
150 };214 };
151 },215 },
152 macho.BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM => {216 macho.BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM => {
153 name.shrinkRetainingCapacity(0);217 var name = std.ArrayList(u8).init(allocator);
154 var next = try reader.readByte();218 var next = try reader.readByte();
155 while (next != @as(u8, 0)) {219 while (next != @as(u8, 0)) {
156 try name.append(next);220 try name.append(next);
157 next = try reader.readByte();221 next = try reader.readByte();
158 }222 }
159 entry.symbol = symbols_by_name.get(name.items[0..]) orelse unreachable;223 symbol.name = name.toOwnedSlice();
160 },224 },
161 macho.BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB => {225 macho.BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB => {
162 entry.segment = imm;226 symbol.segment = imm;
163 entry.offset = try leb.readILEB128(i64, reader);227 symbol.offset = try leb.readILEB128(i64, reader);
164 },228 },
165 macho.BIND_OPCODE_SET_DYLIB_SPECIAL_IMM, macho.BIND_OPCODE_SET_DYLIB_ORDINAL_IMM => {229 macho.BIND_OPCODE_SET_DYLIB_SPECIAL_IMM, macho.BIND_OPCODE_SET_DYLIB_ORDINAL_IMM => {
166 entry.dylib_ordinal = imm;230 symbol.dylib_ordinal = imm;
167 },231 },
168 macho.BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB => {232 macho.BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB => {
169 entry.dylib_ordinal = try leb.readILEB128(i64, reader);233 symbol.dylib_ordinal = try leb.readILEB128(i64, reader);
170 },234 },
171 else => {235 else => {
172 std.log.warn("unhandled BIND_OPCODE_: 0x{x}", .{opcode});236 std.log.warn("unhandled BIND_OPCODE_: 0x{x}", .{opcode});
...@@ -176,5 +240,51 @@ pub const LazyBindingInfoTable = struct {...@@ -176,5 +240,51 @@ pub const LazyBindingInfoTable = struct {
176 assert(done);240 assert(done);
177 }241 }
178242
179 pub fn write(self: LazyBindingInfoTable, writer: anytype) !void {}243 /// Write the binding info table to byte stream.
244 pub fn write(self: LazyBindingInfoTable, writer: anytype) !void {
245 for (self.symbols.items) |symbol| {
246 try writer.writeByte(macho.BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB | @truncate(u4, symbol.segment));
247 try leb.writeILEB128(writer, symbol.offset);
248
249 if (symbol.dylib_ordinal > 15) {
250 try writer.writeByte(macho.BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB);
251 try leb.writeULEB128(writer, @bitCast(u64, symbol.dylib_ordinal));
252 } else if (symbol.dylib_ordinal > 0) {
253 try writer.writeByte(macho.BIND_OPCODE_SET_DYLIB_ORDINAL_IMM | @truncate(u4, @bitCast(u64, symbol.dylib_ordinal)));
254 } else {
255 try writer.writeByte(macho.BIND_OPCODE_SET_DYLIB_SPECIAL_IMM | @truncate(u4, @bitCast(u64, symbol.dylib_ordinal)));
256 }
257
258 if (symbol.name) |name| {
259 try writer.writeByte(macho.BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM); // TODO Sometimes we might want to add flags.
260 try writer.writeAll(name);
261 try writer.writeByte(0);
262 }
263
264 try writer.writeByte(macho.BIND_OPCODE_DO_BIND);
265 try writer.writeByte(macho.BIND_OPCODE_DONE);
266 }
267 }
268
269 /// Calculate size in bytes of this binding info table.
270 pub fn calcSize(self: *LazyBindingInfoTable) usize {
271 var size: usize = 0;
272
273 for (self.symbols.items) |symbol| {
274 size += 1;
275 size += sizeLEB128(symbol.offset);
276 size += 1;
277 if (symbol.dylib_ordinal > 15) {
278 size += sizeLEB128(symbol.dylib_ordinal);
279 }
280 if (symbol.name) |name| {
281 size += 1;
282 size += name.len;
283 size += 1;
284 }
285 size += 2;
286 }
287
288 return size;
289 }
180};290};