| ... | @@ -25,6 +25,7 @@ const Trie = @import("MachO/Trie.zig"); | ... | @@ -25,6 +25,7 @@ const Trie = @import("MachO/Trie.zig"); |
| 25 | const CodeSignature = @import("MachO/CodeSignature.zig"); | 25 | const CodeSignature = @import("MachO/CodeSignature.zig"); |
| 26 | | 26 | |
| 27 | usingnamespace @import("MachO/commands.zig"); | 27 | usingnamespace @import("MachO/commands.zig"); |
| | 28 | usingnamespace @import("MachO/imports.zig"); |
| 28 | | 29 | |
| 29 | pub const base_tag: File.Tag = File.Tag.macho; | 30 | pub const base_tag: File.Tag = File.Tag.macho; |
| 30 | | 31 | |
| ... | @@ -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. |
| 105 | offset_table: std.ArrayListUnmanaged(u64) = .{}, | 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 | error_flags: File.ErrorFlags = File.ErrorFlags{}, | 113 | error_flags: File.ErrorFlags = File.ErrorFlags{}, |
| 108 | | 114 | |
| 109 | cmd_table_dirty: bool = false, | 115 | cmd_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 | } |
| 827 | | 833 | |
| 828 | // Parse dyld info | 834 | // 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 header | 854 | // 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 | } |
| 901 | | 924 | |
| 902 | pub fn deinit(self: *MachO) void { | 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 | 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); |
| 2095 | | 2120 | |
| 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 | } |
| 2100 | | 2124 | |
| 2101 | fn parseBindingInfo(self: *MachO) !void { | 2125 | fn 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); |
| 2107 | | 2131 | |
| 2108 | try parseBindingInfos(self, buffer); | 2132 | var stream = std.io.fixedBufferStream(buffer); |
| 2109 | | 2133 | 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 | } |
| 2114 | | 2135 | |
| 2115 | fn parseLazyBindingInfo(self: *MachO) !void { | 2136 | fn 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); |
| 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 | 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 | | | |
| 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 | } | | |
| 2279 | } | 2145 | } |