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 {
810810 if (self.symtab_cmd_index == null or self.dysymtab_cmd_index == null) {
811811 std.log.err("Incomplete Mach-O binary: no LC_SYMTAB or LC_DYSYMTAB load command found!", .{});
812812 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;
814814 }
815815
816816 // Parse symbol and string tables.
817817 try self.parseSymbolTable();
818818 try self.parseStringTable();
819819
820 std.debug.print("Undef symbols\n", .{});
821 for (self.undef_symbols.items) |sym| {
822 const name = self.string_table.items[sym.n_strx..];
823 const len = blk: {
824 var end: usize = 0;
825 while (true) {
826 if (name[end] == @as(u8, 0)) break;
827 end += 1;
828 }
829 break :blk end;
830 };
831 std.debug.print("name={},sym={}\n", .{ name[0..len], sym });
820 // Parse dyld info
821 try self.parseBindingInfoTable();
822 try self.parseLazyBindingInfoTable();
823
824 // Update the dylib ordinals.
825 self.binding_info_table.dylib_ordinal = next_ordinal;
826 for (self.lazy_binding_info_table.symbols.items) |*symbol| {
827 symbol.dylib_ordinal = next_ordinal;
832828 }
833829
834 // Parse dyld info
835 var symbols_by_name = std.StringHashMap(u16).init(self.base.allocator);
836 defer symbols_by_name.deinit();
837 try symbols_by_name.ensureCapacity(@intCast(u32, self.undef_symbols.items.len));
838
839 for (self.undef_symbols.items) |sym, i| {
840 const name = self.string_table.items[sym.n_strx..];
841 const len = blk: {
842 var end: usize = 0;
843 while (true) {
844 if (name[end] == @as(u8, 0)) break;
845 end += 1;
846 }
847 break :blk end;
848 };
849 symbols_by_name.putAssumeCapacityNoClobber(name[0..len], @intCast(u16, i));
830 // Write update dyld info
831 const dyld_info = self.load_commands.items[self.dyld_info_cmd_index.?].DyldInfoOnly;
832 {
833 const size = self.binding_info_table.calcSize();
834 assert(dyld_info.bind_size == size);
835
836 var buffer = try self.base.allocator.alloc(u8, size);
837 defer self.base.allocator.free(buffer);
838
839 var stream = std.io.fixedBufferStream(buffer);
840 try self.binding_info_table.write(stream.writer());
841
842 try self.base.file.?.pwriteAll(buffer, dyld_info.bind_off);
843 }
844 {
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);
850855 }
851856
852 try self.parseBindingInfoTable(symbols_by_name);
853 try self.parseLazyBindingInfoTable(symbols_by_name);
854857 // Write updated load commands and the header
855858 try self.writeLoadCommands();
856859 try self.writeHeader();
......@@ -1952,6 +1955,68 @@ fn writeExportTrie(self: *MachO) !void {
19521955 self.cmd_table_dirty = true;
19531956}
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
19552020fn writeStringTable(self: *MachO) !void {
19562021 const symtab = &self.load_commands.items[self.symtab_cmd_index.?].Symtab;
19572022 const needed_size = self.string_table.items.len;
......@@ -2122,7 +2187,7 @@ fn parseStringTable(self: *MachO) !void {
21222187 self.string_table.appendSliceAssumeCapacity(buffer);
21232188}
21242189
2125fn parseBindingInfoTable(self: *MachO, symbols_by_name: std.StringHashMap(u16)) !void {
2190fn parseBindingInfoTable(self: *MachO) !void {
21262191 const dyld_info = self.load_commands.items[self.dyld_info_cmd_index.?].DyldInfoOnly;
21272192 var buffer = try self.base.allocator.alloc(u8, dyld_info.bind_size);
21282193 defer self.base.allocator.free(buffer);
......@@ -2130,10 +2195,10 @@ fn parseBindingInfoTable(self: *MachO, symbols_by_name: std.StringHashMap(u16))
21302195 assert(nread == buffer.len);
21312196
21322197 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);
21342199}
21352200
2136fn parseLazyBindingInfoTable(self: *MachO, symbols_by_name: std.StringHashMap(u16)) !void {
2201fn parseLazyBindingInfoTable(self: *MachO) !void {
21372202 const dyld_info = self.load_commands.items[self.dyld_info_cmd_index.?].DyldInfoOnly;
21382203 var buffer = try self.base.allocator.alloc(u8, dyld_info.lazy_bind_size);
21392204 defer self.base.allocator.free(buffer);
......@@ -2141,5 +2206,17 @@ fn parseLazyBindingInfoTable(self: *MachO, symbols_by_name: std.StringHashMap(u1
21412206 assert(nread == buffer.len);
21422207
21432208 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;
21452222}
src/link/MachO/Trie.zig+6-19
......@@ -38,6 +38,7 @@ const macho = std.macho;
3838const testing = std.testing;
3939const assert = std.debug.assert;
4040const Allocator = mem.Allocator;
41const sizeLEB128 = @import("../MachO.zig").sizeLEB128;
4142
4243pub const Node = struct {
4344 base: *Trie,
......@@ -244,9 +245,9 @@ pub const Node = struct {
244245 fn finalize(self: *Node, offset_in_trie: usize) FinalizeResult {
245246 var node_size: usize = 0;
246247 if (self.terminal_info) |info| {
247 node_size += sizeULEB128Mem(info.export_flags);
248 node_size += sizeULEB128Mem(info.vmaddr_offset);
249 node_size += sizeULEB128Mem(node_size);
248 node_size += sizeLEB128(info.export_flags);
249 node_size += sizeLEB128(info.vmaddr_offset);
250 node_size += sizeLEB128(node_size);
250251 } else {
251252 node_size += 1; // 0x0 for non-terminal nodes
252253 }
......@@ -254,7 +255,7 @@ pub const Node = struct {
254255
255256 for (self.edges.items) |edge| {
256257 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);
258259 }
259260
260261 const trie_offset = self.trie_offset orelse 0;
......@@ -264,18 +265,6 @@ pub const Node = struct {
264265
265266 return .{ .node_size = node_size, .updated = updated };
266267 }
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 }
279268};
280269
281270/// The root node of the trie.
......@@ -380,9 +369,7 @@ pub fn read(self: *Trie, reader: anytype) ReadError!usize {
380369}
381370
382371/// Write the trie to a byte stream.
383/// Caller owns the memory and needs to free it.
384/// Panics if the trie was not finalized using `finalize`
385/// before calling this method.
372/// Panics if the trie was not finalized using `finalize` before calling this method.
386373pub fn write(self: Trie, writer: anytype) !usize {
387374 assert(!self.trie_dirty);
388375 var counting_writer = std.io.countingWriter(writer);
src/link/MachO/imports.zig+149-39
......@@ -5,16 +5,22 @@ const mem = std.mem;
55
66const assert = std.debug.assert;
77const 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.
912pub const BindingInfoTable = struct {
13 /// Id of the dynamic library where the specified entries can be found.
1014 dylib_ordinal: i64 = 0,
15
16 /// Binding type; defaults to pointer type.
1117 binding_type: u8 = macho.BIND_TYPE_POINTER,
12 entries: std.ArrayListUnmanaged(Entry) = .{},
1318
14 pub const Entry = struct {
15 /// Id of the symbol in the undef symbol table.
16 /// Can be null.
17 symbol: ?u16 = null,
19 symbols: std.ArrayListUnmanaged(Symbol) = .{},
20
21 pub const Symbol = struct {
22 /// Symbol name.
23 name: ?[]u8 = null,
1824
1925 /// Id of the segment where to bind this symbol to.
2026 segment: u8,
......@@ -24,14 +30,17 @@ pub const BindingInfoTable = struct {
2430 };
2531
2632 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);
2839 }
2940
30 pub fn read(self: *BindingInfoTable, allocator: *Allocator, symbols_by_name: anytype, reader: anytype) !void {
31 var name = std.ArrayList(u8).init(allocator);
32 defer name.deinit();
33
34 var entry: Entry = .{
41 /// Parse the binding info table from byte stream.
42 pub fn read(self: *BindingInfoTable, reader: anytype, allocator: *Allocator) !void {
43 var symbol: Symbol = .{
3544 .segment = 0,
3645 .offset = 0,
3746 };
......@@ -48,8 +57,8 @@ pub const BindingInfoTable = struct {
4857
4958 switch (opcode) {
5059 macho.BIND_OPCODE_DO_BIND => {
51 try self.entries.append(allocator, entry);
52 entry = .{
60 try self.symbols.append(allocator, symbol);
61 symbol = .{
5362 .segment = 0,
5463 .offset = 0,
5564 };
......@@ -59,17 +68,17 @@ pub const BindingInfoTable = struct {
5968 break;
6069 },
6170 macho.BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM => {
62 name.shrinkRetainingCapacity(0);
71 var name = std.ArrayList(u8).init(allocator);
6372 var next = try reader.readByte();
6473 while (next != @as(u8, 0)) {
6574 try name.append(next);
6675 next = try reader.readByte();
6776 }
68 entry.symbol = symbols_by_name.get(name.items[0..]);
77 symbol.name = name.toOwnedSlice();
6978 },
7079 macho.BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB => {
71 entry.segment = imm;
72 entry.offset = try leb.readILEB128(i64, reader);
80 symbol.segment = imm;
81 symbol.offset = try leb.readILEB128(i64, reader);
7382 },
7483 macho.BIND_OPCODE_SET_DYLIB_SPECIAL_IMM, macho.BIND_OPCODE_SET_DYLIB_ORDINAL_IMM => {
7584 assert(!dylib_ordinal_set);
......@@ -90,15 +99,69 @@ pub const BindingInfoTable = struct {
9099 assert(done);
91100 }
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 }
94155};
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.
96159pub const LazyBindingInfoTable = struct {
97 entries: std.ArrayListUnmanaged(Entry) = .{},
160 symbols: std.ArrayListUnmanaged(Symbol) = .{},
98161
99 pub const Entry = struct {
100 /// Id of the symbol in the undef symbol table.
101 symbol: u16,
162 pub const Symbol = struct {
163 /// Symbol name.
164 name: ?[]u8 = null,
102165
103166 /// Offset of this symbol wrt to the segment id encoded in `segment`.
104167 offset: i64,
......@@ -113,15 +176,17 @@ pub const LazyBindingInfoTable = struct {
113176 };
114177
115178 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);
117185 }
118186
119 pub fn read(self: *LazyBindingInfoTable, allocator: *Allocator, symbols_by_name: anytype, reader: anytype) !void {
120 var name = std.ArrayList(u8).init(allocator);
121 defer name.deinit();
122
123 var entry: Entry = .{
124 .symbol = 0,
187 /// Parse the binding info table from byte stream.
188 pub fn read(self: *LazyBindingInfoTable, reader: anytype, allocator: *Allocator) !void {
189 var symbol: Symbol = .{
125190 .offset = 0,
126191 .segment = 0,
127192 .dylib_ordinal = 0,
......@@ -138,35 +203,34 @@ pub const LazyBindingInfoTable = struct {
138203
139204 switch (opcode) {
140205 macho.BIND_OPCODE_DO_BIND => {
141 try self.entries.append(allocator, entry);
206 try self.symbols.append(allocator, symbol);
142207 },
143208 macho.BIND_OPCODE_DONE => {
144209 done = true;
145 entry = .{
146 .symbol = 0,
210 symbol = .{
147211 .offset = 0,
148212 .segment = 0,
149213 .dylib_ordinal = 0,
150214 };
151215 },
152216 macho.BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM => {
153 name.shrinkRetainingCapacity(0);
217 var name = std.ArrayList(u8).init(allocator);
154218 var next = try reader.readByte();
155219 while (next != @as(u8, 0)) {
156220 try name.append(next);
157221 next = try reader.readByte();
158222 }
159 entry.symbol = symbols_by_name.get(name.items[0..]) orelse unreachable;
223 symbol.name = name.toOwnedSlice();
160224 },
161225 macho.BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB => {
162 entry.segment = imm;
163 entry.offset = try leb.readILEB128(i64, reader);
226 symbol.segment = imm;
227 symbol.offset = try leb.readILEB128(i64, reader);
164228 },
165229 macho.BIND_OPCODE_SET_DYLIB_SPECIAL_IMM, macho.BIND_OPCODE_SET_DYLIB_ORDINAL_IMM => {
166 entry.dylib_ordinal = imm;
230 symbol.dylib_ordinal = imm;
167231 },
168232 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);
170234 },
171235 else => {
172236 std.log.warn("unhandled BIND_OPCODE_: 0x{x}", .{opcode});
......@@ -176,5 +240,51 @@ pub const LazyBindingInfoTable = struct {
176240 assert(done);
177241 }
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 }
180290};