| ... | ... | @@ -25,6 +25,7 @@ const Trie = @import("MachO/Trie.zig"); |
| 25 | 25 | const CodeSignature = @import("MachO/CodeSignature.zig"); |
| 26 | 26 | |
| 27 | 27 | usingnamespace @import("MachO/commands.zig"); |
| 28 | usingnamespace @import("MachO/imports.zig"); |
| 28 | 29 | |
| 29 | 30 | pub const base_tag: File.Tag = File.Tag.macho; |
| 30 | 31 | |
| ... | ... | @@ -104,6 +105,11 @@ string_table: std.ArrayListUnmanaged(u8) = .{}, |
| 104 | 105 | /// table needs to be rewritten. |
| 105 | 106 | offset_table: std.ArrayListUnmanaged(u64) = .{}, |
| 106 | 107 | |
| 108 | /// Table of binding info entries. |
| 109 | binding_info_table: BindingInfoTable = .{}, |
| 110 | /// Table of lazy binding info entries. |
| 111 | lazy_binding_info_table: LazyBindingInfoTable = .{}, |
| 112 | |
| 107 | 113 | error_flags: File.ErrorFlags = File.ErrorFlags{}, |
| 108 | 114 | |
| 109 | 115 | cmd_table_dirty: bool = false, |
| ... | ... | @@ -826,8 +832,25 @@ fn linkWithLLD(self: *MachO, comp: *Compilation) !void { |
| 826 | 832 | } |
| 827 | 833 | |
| 828 | 834 | // Parse dyld info |
| 829 | | try self.parseBindingInfo(); |
| 830 | | try self.parseLazyBindingInfo(); |
| 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)); |
| 850 | } |
| 851 | |
| 852 | try self.parseBindingInfoTable(symbols_by_name); |
| 853 | try self.parseLazyBindingInfoTable(symbols_by_name); |
| 831 | 854 | // Write updated load commands and the header |
| 832 | 855 | try self.writeLoadCommands(); |
| 833 | 856 | try self.writeHeader(); |
| ... | ... | @@ -900,6 +923,8 @@ fn darwinArchString(arch: std.Target.Cpu.Arch) []const u8 { |
| 900 | 923 | } |
| 901 | 924 | |
| 902 | 925 | pub 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 | 928 | self.pie_fixups.deinit(self.base.allocator); |
| 904 | 929 | self.text_block_free_list.deinit(self.base.allocator); |
| 905 | 930 | self.offset_table.deinit(self.base.allocator); |
| ... | ... | @@ -2094,186 +2119,27 @@ fn parseStringTable(self: *MachO) !void { |
| 2094 | 2119 | assert(nread == buffer.len); |
| 2095 | 2120 | |
| 2096 | 2121 | try self.string_table.ensureCapacity(self.base.allocator, symtab.strsize); |
| 2097 | | |
| 2098 | 2122 | self.string_table.appendSliceAssumeCapacity(buffer); |
| 2099 | 2123 | } |
| 2100 | 2124 | |
| 2101 | | fn parseBindingInfo(self: *MachO) !void { |
| 2125 | fn parseBindingInfoTable(self: *MachO, symbols_by_name: std.StringHashMap(u16)) !void { |
| 2102 | 2126 | const dyld_info = self.load_commands.items[self.dyld_info_cmd_index.?].DyldInfoOnly; |
| 2103 | 2127 | var buffer = try self.base.allocator.alloc(u8, dyld_info.bind_size); |
| 2104 | 2128 | defer self.base.allocator.free(buffer); |
| 2105 | 2129 | const nread = try self.base.file.?.preadAll(buffer, dyld_info.bind_off); |
| 2106 | 2130 | assert(nread == buffer.len); |
| 2107 | 2131 | |
| 2108 | | try parseBindingInfos(self, buffer); |
| 2109 | | |
| 2110 | | if (try parseAndFixupBindingInfoBuffer(self.base.allocator, buffer)) { |
| 2111 | | try self.base.file.?.pwriteAll(buffer, dyld_info.bind_off); |
| 2112 | | } |
| 2132 | var stream = std.io.fixedBufferStream(buffer); |
| 2133 | try self.binding_info_table.read(self.base.allocator, symbols_by_name, stream.reader()); |
| 2113 | 2134 | } |
| 2114 | 2135 | |
| 2115 | | fn parseLazyBindingInfo(self: *MachO) !void { |
| 2136 | fn parseLazyBindingInfoTable(self: *MachO, symbols_by_name: std.StringHashMap(u16)) !void { |
| 2116 | 2137 | const dyld_info = self.load_commands.items[self.dyld_info_cmd_index.?].DyldInfoOnly; |
| 2117 | 2138 | var buffer = try self.base.allocator.alloc(u8, dyld_info.lazy_bind_size); |
| 2118 | 2139 | defer self.base.allocator.free(buffer); |
| 2119 | 2140 | const nread = try self.base.file.?.preadAll(buffer, dyld_info.lazy_bind_off); |
| 2120 | 2141 | assert(nread == buffer.len); |
| 2121 | 2142 | |
| 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 | | |
| 2129 | | fn parseAndFixupBindingInfoBuffer(allocator: *Allocator, buffer: []u8) !bool { |
| 2130 | 2143 | var stream = std.io.fixedBufferStream(buffer); |
| 2131 | | var reader = 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 | | |
| 2179 | | const BindingEntry = struct { |
| 2180 | | symbol: ?u16 = null, |
| 2181 | | offset: i64, |
| 2182 | | dylib_ordinal: ?i64 = null, |
| 2183 | | segment: u8, |
| 2184 | | bind_type: u8, |
| 2185 | | }; |
| 2186 | | |
| 2187 | | fn 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 | | } |
| 2144 | try self.lazy_binding_info_table.read(self.base.allocator, symbols_by_name, stream.reader()); |
| 2279 | 2145 | } |