authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2020-12-13 17:20:03+01:00
committergravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2020-12-17 10:04:53+01:00
log5e913c9c2c41e44620911ffce0ebc20d9af041ca
treef892eb6ff07a0b06b9217b2b7d37eabf1d5157ed
parent46b2a08d067fd9e4a0b499e1da69bd0be29aaa5a

macho: move (lazy)binding tables into imports module


2 files changed, 212 insertions(+), 166 deletions(-)

src/link/MachO.zig+32-166
...@@ -25,6 +25,7 @@ const Trie = @import("MachO/Trie.zig");...@@ -25,6 +25,7 @@ const Trie = @import("MachO/Trie.zig");
25const CodeSignature = @import("MachO/CodeSignature.zig");25const CodeSignature = @import("MachO/CodeSignature.zig");
2626
27usingnamespace @import("MachO/commands.zig");27usingnamespace @import("MachO/commands.zig");
28usingnamespace @import("MachO/imports.zig");
2829
29pub const base_tag: File.Tag = File.Tag.macho;30pub const base_tag: File.Tag = File.Tag.macho;
3031
...@@ -104,6 +105,11 @@ string_table: std.ArrayListUnmanaged(u8) = .{},...@@ -104,6 +105,11 @@ string_table: std.ArrayListUnmanaged(u8) = .{},
104/// table needs to be rewritten.105/// table needs to be rewritten.
105offset_table: std.ArrayListUnmanaged(u64) = .{},106offset_table: std.ArrayListUnmanaged(u64) = .{},
106107
108/// Table of binding info entries.
109binding_info_table: BindingInfoTable = .{},
110/// Table of lazy binding info entries.
111lazy_binding_info_table: LazyBindingInfoTable = .{},
112
107error_flags: File.ErrorFlags = File.ErrorFlags{},113error_flags: File.ErrorFlags = File.ErrorFlags{},
108114
109cmd_table_dirty: bool = false,115cmd_table_dirty: bool = false,
...@@ -826,8 +832,25 @@ fn linkWithLLD(self: *MachO, comp: *Compilation) !void {...@@ -826,8 +832,25 @@ fn linkWithLLD(self: *MachO, comp: *Compilation) !void {
826 }832 }
827833
828 // Parse dyld info834 // Parse dyld info
829 try self.parseBindingInfo();835 var symbols_by_name = std.StringHashMap(u16).init(self.base.allocator);
830 try self.parseLazyBindingInfo();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));
850 }
851
852 try self.parseBindingInfoTable(symbols_by_name);
853 try self.parseLazyBindingInfoTable(symbols_by_name);
831 // Write updated load commands and the header854 // Write updated load commands and the header
832 try self.writeLoadCommands();855 try self.writeLoadCommands();
833 try self.writeHeader();856 try self.writeHeader();
...@@ -900,6 +923,8 @@ fn darwinArchString(arch: std.Target.Cpu.Arch) []const u8 {...@@ -900,6 +923,8 @@ fn darwinArchString(arch: std.Target.Cpu.Arch) []const u8 {
900}923}
901924
902pub fn deinit(self: *MachO) void {925pub fn deinit(self: *MachO) void {
926 self.binding_info_table.deinit(self.base.allocator);
927 self.lazy_binding_info_table.deinit(self.base.allocator);
903 self.pie_fixups.deinit(self.base.allocator);928 self.pie_fixups.deinit(self.base.allocator);
904 self.text_block_free_list.deinit(self.base.allocator);929 self.text_block_free_list.deinit(self.base.allocator);
905 self.offset_table.deinit(self.base.allocator);930 self.offset_table.deinit(self.base.allocator);
...@@ -2094,186 +2119,27 @@ fn parseStringTable(self: *MachO) !void {...@@ -2094,186 +2119,27 @@ fn parseStringTable(self: *MachO) !void {
2094 assert(nread == buffer.len);2119 assert(nread == buffer.len);
20952120
2096 try self.string_table.ensureCapacity(self.base.allocator, symtab.strsize);2121 try self.string_table.ensureCapacity(self.base.allocator, symtab.strsize);
2097
2098 self.string_table.appendSliceAssumeCapacity(buffer);2122 self.string_table.appendSliceAssumeCapacity(buffer);
2099}2123}
21002124
2101fn parseBindingInfo(self: *MachO) !void {2125fn parseBindingInfoTable(self: *MachO, symbols_by_name: std.StringHashMap(u16)) !void {
2102 const dyld_info = self.load_commands.items[self.dyld_info_cmd_index.?].DyldInfoOnly;2126 const dyld_info = self.load_commands.items[self.dyld_info_cmd_index.?].DyldInfoOnly;
2103 var buffer = try self.base.allocator.alloc(u8, dyld_info.bind_size);2127 var buffer = try self.base.allocator.alloc(u8, dyld_info.bind_size);
2104 defer self.base.allocator.free(buffer);2128 defer self.base.allocator.free(buffer);
2105 const nread = try self.base.file.?.preadAll(buffer, dyld_info.bind_off);2129 const nread = try self.base.file.?.preadAll(buffer, dyld_info.bind_off);
2106 assert(nread == buffer.len);2130 assert(nread == buffer.len);
21072131
2108 try parseBindingInfos(self, buffer);2132 var stream = std.io.fixedBufferStream(buffer);
21092133 try self.binding_info_table.read(self.base.allocator, symbols_by_name, stream.reader());
2110 if (try parseAndFixupBindingInfoBuffer(self.base.allocator, buffer)) {
2111 try self.base.file.?.pwriteAll(buffer, dyld_info.bind_off);
2112 }
2113}2134}
21142135
2115fn parseLazyBindingInfo(self: *MachO) !void {2136fn parseLazyBindingInfoTable(self: *MachO, symbols_by_name: std.StringHashMap(u16)) !void {
2116 const dyld_info = self.load_commands.items[self.dyld_info_cmd_index.?].DyldInfoOnly;2137 const dyld_info = self.load_commands.items[self.dyld_info_cmd_index.?].DyldInfoOnly;
2117 var buffer = try self.base.allocator.alloc(u8, dyld_info.lazy_bind_size);2138 var buffer = try self.base.allocator.alloc(u8, dyld_info.lazy_bind_size);
2118 defer self.base.allocator.free(buffer);2139 defer self.base.allocator.free(buffer);
2119 const nread = try self.base.file.?.preadAll(buffer, dyld_info.lazy_bind_off);2140 const nread = try self.base.file.?.preadAll(buffer, dyld_info.lazy_bind_off);
2120 assert(nread == buffer.len);2141 assert(nread == buffer.len);
21212142
2122 try parseBindingInfos(self, buffer);
2123
2124 if (try parseAndFixupBindingInfoBuffer(self.base.allocator, buffer)) {
2125 try self.base.file.?.pwriteAll(buffer, dyld_info.lazy_bind_off);
2126 }
2127}
2128
2129fn parseAndFixupBindingInfoBuffer(allocator: *Allocator, buffer: []u8) !bool {
2130 var stream = std.io.fixedBufferStream(buffer);2143 var stream = std.io.fixedBufferStream(buffer);
2131 var reader = stream.reader();2144 try self.lazy_binding_info_table.read(self.base.allocator, symbols_by_name, stream.reader());
2132 var done = false;
2133 var fixups = std.ArrayList(usize).init(allocator);
2134 defer fixups.deinit();
2135
2136 while (true) {
2137 const inst = reader.readByte() catch |err| switch (err) {
2138 error.EndOfStream => break,
2139 else => return err,
2140 };
2141 const imm: u8 = inst & macho.BIND_IMMEDIATE_MASK;
2142 const opcode: u8 = inst & macho.BIND_OPCODE_MASK;
2143 switch (opcode) {
2144 macho.BIND_OPCODE_DONE => {
2145 done = true; // TODO There appear to be multiple BIND_OPCODE_DONE in lazy binding info...
2146 },
2147 macho.BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM => {
2148 var next = try reader.readByte();
2149 while (next != @as(u8, 0)) {
2150 next = try reader.readByte();
2151 }
2152 },
2153 macho.BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB => {
2154 const uleb_enc = try std.leb.readULEB128(u64, reader);
2155 },
2156 macho.BIND_OPCODE_SET_DYLIB_SPECIAL_IMM => {
2157 // We note the position in the stream to fixup later.
2158 const pos = try reader.context.getPos();
2159 try fixups.append(pos - 1);
2160 },
2161 else => {},
2162 }
2163 }
2164 assert(done);
2165
2166 var buffer_dirty = false;
2167 try stream.seekTo(0);
2168 var writer = stream.writer();
2169 for (fixups.items) |pos| {
2170 try writer.context.seekTo(pos);
2171 const inst = macho.BIND_OPCODE_SET_DYLIB_ORDINAL_IMM | 1;
2172 _ = try writer.write(&[_]u8{inst});
2173 buffer_dirty = true;
2174 }
2175
2176 return buffer_dirty;
2177}
2178
2179const BindingEntry = struct {
2180 symbol: ?u16 = null,
2181 offset: i64,
2182 dylib_ordinal: ?i64 = null,
2183 segment: u8,
2184 bind_type: u8,
2185};
2186
2187fn parseBindingInfos(self: *MachO, buffer: []u8) !void {
2188 var symbolsByName = std.StringHashMap(u16).init(self.base.allocator);
2189 defer symbolsByName.deinit();
2190 try symbolsByName.ensureCapacity(@intCast(u32, self.undef_symbols.items.len));
2191
2192 for (self.undef_symbols.items) |sym, i| {
2193 const name = self.string_table.items[sym.n_strx..];
2194 const len = blk: {
2195 var end: usize = 0;
2196 while (true) {
2197 if (name[end] == @as(u8, 0)) break;
2198 end += 1;
2199 }
2200 break :blk end;
2201 };
2202 symbolsByName.putAssumeCapacityNoClobber(name[0..len], @intCast(u16, i));
2203 }
2204
2205 var stream = std.io.fixedBufferStream(buffer);
2206 var reader = stream.reader();
2207 var done = false;
2208
2209 var name = std.ArrayList(u8).init(self.base.allocator);
2210 defer name.deinit();
2211
2212 var entries = std.ArrayList(BindingEntry).init(self.base.allocator);
2213 defer entries.deinit();
2214 var dylib_ordinal: i64 = 0;
2215
2216 var entry: BindingEntry = .{
2217 .offset = 0,
2218 .segment = 0,
2219 .bind_type = 0,
2220 };
2221
2222 while (true) {
2223 const inst = reader.readByte() catch |err| switch (err) {
2224 error.EndOfStream => break,
2225 else => return err,
2226 };
2227 const imm: u8 = inst & macho.BIND_IMMEDIATE_MASK;
2228 const opcode: u8 = inst & macho.BIND_OPCODE_MASK;
2229
2230 switch (opcode) {
2231 macho.BIND_OPCODE_DO_BIND => {
2232 if (entry.dylib_ordinal == null) {
2233 entry.dylib_ordinal = dylib_ordinal;
2234 }
2235 try entries.append(entry);
2236 entry = .{
2237 .offset = 0,
2238 .segment = 0,
2239 .bind_type = 0,
2240 };
2241 },
2242 macho.BIND_OPCODE_DONE => {
2243 done = true;
2244 },
2245 macho.BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM => {
2246 name.shrinkRetainingCapacity(0);
2247 var next = try reader.readByte();
2248 while (next != @as(u8, 0)) {
2249 try name.append(next);
2250 next = try reader.readByte();
2251 }
2252 std.debug.print("name={}\n", .{name.items});
2253 entry.symbol = symbolsByName.get(name.items[0..]);
2254 },
2255 macho.BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB => {
2256 entry.segment = imm;
2257 entry.offset = try std.leb.readILEB128(i64, reader);
2258 },
2259 macho.BIND_OPCODE_SET_DYLIB_SPECIAL_IMM, macho.BIND_OPCODE_SET_DYLIB_ORDINAL_IMM => {
2260 entry.dylib_ordinal = imm;
2261 },
2262 macho.BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB => {
2263 dylib_ordinal = try std.leb.readILEB128(i64, reader);
2264 entry.dylib_ordinal = dylib_ordinal;
2265 },
2266 macho.BIND_OPCODE_SET_TYPE_IMM => {
2267 entry.bind_type = imm;
2268 },
2269 else => {
2270 std.log.warn("unhandled BIND_OPCODE_: 0x{x}", .{opcode});
2271 },
2272 }
2273 }
2274 assert(done);
2275
2276 for (entries.items) |e| {
2277 std.debug.print("entry={}\n", .{e});
2278 }
2279}2145}
src/link/MachO/imports.zig created+180
...@@ -0,0 +1,180 @@
1const std = @import("std");
2const leb = std.leb;
3const macho = std.macho;
4const mem = std.mem;
5
6const assert = std.debug.assert;
7const Allocator = mem.Allocator;
8
9pub const BindingInfoTable = struct {
10 dylib_ordinal: i64 = 0,
11 binding_type: u8 = macho.BIND_TYPE_POINTER,
12 entries: std.ArrayListUnmanaged(Entry) = .{},
13
14 pub const Entry = struct {
15 /// Id of the symbol in the undef symbol table.
16 /// Can be null.
17 symbol: ?u16 = null,
18
19 /// Id of the segment where to bind this symbol to.
20 segment: u8,
21
22 /// Offset of this symbol wrt to the segment id encoded in `segment`.
23 offset: i64,
24 };
25
26 pub fn deinit(self: *BindingInfoTable, allocator: *Allocator) void {
27 self.entries.deinit(allocator);
28 }
29
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 = .{
35 .segment = 0,
36 .offset = 0,
37 };
38
39 var dylib_ordinal_set = false;
40 var done = false;
41 while (true) {
42 const inst = reader.readByte() catch |err| switch (err) {
43 error.EndOfStream => break,
44 else => return err,
45 };
46 const imm: u8 = inst & macho.BIND_IMMEDIATE_MASK;
47 const opcode: u8 = inst & macho.BIND_OPCODE_MASK;
48
49 switch (opcode) {
50 macho.BIND_OPCODE_DO_BIND => {
51 try self.entries.append(allocator, entry);
52 entry = .{
53 .segment = 0,
54 .offset = 0,
55 };
56 },
57 macho.BIND_OPCODE_DONE => {
58 done = true;
59 break;
60 },
61 macho.BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM => {
62 name.shrinkRetainingCapacity(0);
63 var next = try reader.readByte();
64 while (next != @as(u8, 0)) {
65 try name.append(next);
66 next = try reader.readByte();
67 }
68 entry.symbol = symbols_by_name.get(name.items[0..]);
69 },
70 macho.BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB => {
71 entry.segment = imm;
72 entry.offset = try leb.readILEB128(i64, reader);
73 },
74 macho.BIND_OPCODE_SET_DYLIB_SPECIAL_IMM, macho.BIND_OPCODE_SET_DYLIB_ORDINAL_IMM => {
75 assert(!dylib_ordinal_set);
76 self.dylib_ordinal = imm;
77 },
78 macho.BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB => {
79 assert(!dylib_ordinal_set);
80 self.dylib_ordinal = try leb.readILEB128(i64, reader);
81 },
82 macho.BIND_OPCODE_SET_TYPE_IMM => {
83 self.binding_type = imm;
84 },
85 else => {
86 std.log.warn("unhandled BIND_OPCODE_: 0x{x}", .{opcode});
87 },
88 }
89 }
90 assert(done);
91 }
92
93 pub fn write(self: BindingInfoTable, writer: anytype) !void {}
94};
95
96pub const LazyBindingInfoTable = struct {
97 entries: std.ArrayListUnmanaged(Entry) = .{},
98
99 pub const Entry = struct {
100 /// Id of the symbol in the undef symbol table.
101 symbol: u16,
102
103 /// Offset of this symbol wrt to the segment id encoded in `segment`.
104 offset: i64,
105
106 /// Id of the dylib where this symbol is expected to reside.
107 /// Positive ordinals point at dylibs imported with LC_LOAD_DYLIB,
108 /// 0 means this binary, -1 the main executable, and -2 flat lookup.
109 dylib_ordinal: i64,
110
111 /// Id of the segment where to bind this symbol to.
112 segment: u8,
113 };
114
115 pub fn deinit(self: *LazyBindingInfoTable, allocator: *Allocator) void {
116 self.entries.deinit(allocator);
117 }
118
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,
125 .offset = 0,
126 .segment = 0,
127 .dylib_ordinal = 0,
128 };
129
130 var done = false;
131 while (true) {
132 const inst = reader.readByte() catch |err| switch (err) {
133 error.EndOfStream => break,
134 else => return err,
135 };
136 const imm: u8 = inst & macho.BIND_IMMEDIATE_MASK;
137 const opcode: u8 = inst & macho.BIND_OPCODE_MASK;
138
139 switch (opcode) {
140 macho.BIND_OPCODE_DO_BIND => {
141 try self.entries.append(allocator, entry);
142 },
143 macho.BIND_OPCODE_DONE => {
144 done = true;
145 entry = .{
146 .symbol = 0,
147 .offset = 0,
148 .segment = 0,
149 .dylib_ordinal = 0,
150 };
151 },
152 macho.BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM => {
153 name.shrinkRetainingCapacity(0);
154 var next = try reader.readByte();
155 while (next != @as(u8, 0)) {
156 try name.append(next);
157 next = try reader.readByte();
158 }
159 entry.symbol = symbols_by_name.get(name.items[0..]) orelse unreachable;
160 },
161 macho.BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB => {
162 entry.segment = imm;
163 entry.offset = try leb.readILEB128(i64, reader);
164 },
165 macho.BIND_OPCODE_SET_DYLIB_SPECIAL_IMM, macho.BIND_OPCODE_SET_DYLIB_ORDINAL_IMM => {
166 entry.dylib_ordinal = imm;
167 },
168 macho.BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB => {
169 entry.dylib_ordinal = try leb.readILEB128(i64, reader);
170 },
171 else => {
172 std.log.warn("unhandled BIND_OPCODE_: 0x{x}", .{opcode});
173 },
174 }
175 }
176 assert(done);
177 }
178
179 pub fn write(self: LazyBindingInfoTable, writer: anytype) !void {}
180};