| ... | @@ -107,6 +107,7 @@ offset_table: std.ArrayListUnmanaged(u64) = .{}, | ... | @@ -107,6 +107,7 @@ offset_table: std.ArrayListUnmanaged(u64) = .{}, |
| 107 | error_flags: File.ErrorFlags = File.ErrorFlags{}, | 107 | error_flags: File.ErrorFlags = File.ErrorFlags{}, |
| 108 | | 108 | |
| 109 | cmd_table_dirty: bool = false, | 109 | cmd_table_dirty: bool = false, |
| | 110 | other_dylibs_present: bool = false, |
| 110 | | 111 | |
| 111 | /// A list of text blocks that have surplus capacity. This list can have false | 112 | /// A list of text blocks that have surplus capacity. This list can have false |
| 112 | /// positives, as functions grow and shrink over time, only sometimes being added | 113 | /// positives, as functions grow and shrink over time, only sometimes being added |
| ... | @@ -755,6 +756,7 @@ fn linkWithLLD(self: *MachO, comp: *Compilation) !void { | ... | @@ -755,6 +756,7 @@ fn linkWithLLD(self: *MachO, comp: *Compilation) !void { |
| 755 | const out_file = try directory.handle.openFile(self.base.options.emit.?.sub_path, .{ .write = true }); | 756 | const out_file = try directory.handle.openFile(self.base.options.emit.?.sub_path, .{ .write = true }); |
| 756 | try self.parseFromFile(out_file); | 757 | try self.parseFromFile(out_file); |
| 757 | if (self.libsystem_cmd_index == null) { | 758 | if (self.libsystem_cmd_index == null) { |
| | 759 | if (self.other_dylibs_present) return; // TODO We cannot handle this situation yet. |
| 758 | const text_segment = self.load_commands.items[self.text_segment_cmd_index.?].Segment; | 760 | const text_segment = self.load_commands.items[self.text_segment_cmd_index.?].Segment; |
| 759 | const text_section = text_segment.sections.items[self.text_section_index.?]; | 761 | const text_section = text_segment.sections.items[self.text_section_index.?]; |
| 760 | const after_last_cmd_offset = self.header.?.sizeofcmds + @sizeOf(macho.mach_header_64); | 762 | const after_last_cmd_offset = self.header.?.sizeofcmds + @sizeOf(macho.mach_header_64); |
| ... | @@ -787,7 +789,9 @@ fn linkWithLLD(self: *MachO, comp: *Compilation) !void { | ... | @@ -787,7 +789,9 @@ fn linkWithLLD(self: *MachO, comp: *Compilation) !void { |
| 787 | mem.set(u8, dylib_cmd.data, 0); | 789 | mem.set(u8, dylib_cmd.data, 0); |
| 788 | mem.copy(u8, dylib_cmd.data, mem.spanZ(LIB_SYSTEM_PATH)); | 790 | mem.copy(u8, dylib_cmd.data, mem.spanZ(LIB_SYSTEM_PATH)); |
| 789 | try self.load_commands.append(self.base.allocator, .{ .Dylib = dylib_cmd }); | 791 | try self.load_commands.append(self.base.allocator, .{ .Dylib = dylib_cmd }); |
| 790 | // TODO Fixup linkedit data | 792 | // Parse dyld info |
| | 793 | try self.parseBindingInfo(); |
| | 794 | try self.parseLazyBindingInfo(); |
| 791 | // Write updated load commands and the header | 795 | // Write updated load commands and the header |
| 792 | try self.writeLoadCommands(); | 796 | try self.writeLoadCommands(); |
| 793 | try self.writeHeader(); | 797 | try self.writeHeader(); |
| ... | @@ -2002,6 +2006,8 @@ fn parseFromFile(self: *MachO, file: fs.File) !void { | ... | @@ -2002,6 +2006,8 @@ fn parseFromFile(self: *MachO, file: fs.File) !void { |
| 2002 | const x = cmd.Dylib; | 2006 | const x = cmd.Dylib; |
| 2003 | if (parseAndCmpName(x.data, mem.spanZ(LIB_SYSTEM_PATH))) { | 2007 | if (parseAndCmpName(x.data, mem.spanZ(LIB_SYSTEM_PATH))) { |
| 2004 | self.libsystem_cmd_index = i; | 2008 | self.libsystem_cmd_index = i; |
| | 2009 | } else { |
| | 2010 | self.other_dylibs_present = true; |
| 2005 | } | 2011 | } |
| 2006 | }, | 2012 | }, |
| 2007 | macho.LC_FUNCTION_STARTS => { | 2013 | macho.LC_FUNCTION_STARTS => { |
| ... | @@ -2030,3 +2036,75 @@ fn parseAndCmpName(name: []const u8, needle: []const u8) bool { | ... | @@ -2030,3 +2036,75 @@ fn parseAndCmpName(name: []const u8, needle: []const u8) bool { |
| 2030 | const len = mem.indexOfScalar(u8, name[0..], @as(u8, 0)) orelse name.len; | 2036 | const len = mem.indexOfScalar(u8, name[0..], @as(u8, 0)) orelse name.len; |
| 2031 | return mem.eql(u8, name[0..len], needle); | 2037 | return mem.eql(u8, name[0..len], needle); |
| 2032 | } | 2038 | } |
| | 2039 | |
| | 2040 | fn parseBindingInfo(self: *MachO) !void { |
| | 2041 | const dyld_info = self.load_commands.items[self.dyld_info_cmd_index.?].DyldInfoOnly; |
| | 2042 | var buffer = try self.base.allocator.alloc(u8, dyld_info.bind_size); |
| | 2043 | defer self.base.allocator.free(buffer); |
| | 2044 | const nread = try self.base.file.?.preadAll(buffer, dyld_info.bind_off); |
| | 2045 | assert(nread == buffer.len); |
| | 2046 | if (try parseAndFixupBindingInfoBuffer(self.base.allocator, buffer)) { |
| | 2047 | try self.base.file.?.pwriteAll(buffer, dyld_info.bind_off); |
| | 2048 | } |
| | 2049 | } |
| | 2050 | |
| | 2051 | fn parseLazyBindingInfo(self: *MachO) !void { |
| | 2052 | const dyld_info = self.load_commands.items[self.dyld_info_cmd_index.?].DyldInfoOnly; |
| | 2053 | var buffer = try self.base.allocator.alloc(u8, dyld_info.lazy_bind_size); |
| | 2054 | defer self.base.allocator.free(buffer); |
| | 2055 | const nread = try self.base.file.?.preadAll(buffer, dyld_info.lazy_bind_off); |
| | 2056 | assert(nread == buffer.len); |
| | 2057 | if (try parseAndFixupBindingInfoBuffer(self.base.allocator, buffer)) { |
| | 2058 | try self.base.file.?.pwriteAll(buffer, dyld_info.lazy_bind_off); |
| | 2059 | } |
| | 2060 | } |
| | 2061 | |
| | 2062 | fn parseAndFixupBindingInfoBuffer(allocator: *Allocator, buffer: []u8) !bool{ |
| | 2063 | var stream = std.io.fixedBufferStream(buffer); |
| | 2064 | var reader = stream.reader(); |
| | 2065 | var done = false; |
| | 2066 | var fixups = std.ArrayList(usize).init(allocator); |
| | 2067 | defer fixups.deinit(); |
| | 2068 | |
| | 2069 | while (true) { |
| | 2070 | const inst = reader.readByte() catch |err| switch (err) { |
| | 2071 | error.EndOfStream => break, |
| | 2072 | else => return err, |
| | 2073 | }; |
| | 2074 | const imm: u8 = inst & macho.BIND_IMMEDIATE_MASK; |
| | 2075 | const opcode: u8 = inst & macho.BIND_OPCODE_MASK; |
| | 2076 | switch (opcode) { |
| | 2077 | macho.BIND_OPCODE_DONE => { |
| | 2078 | done = true; // TODO There appear to be multiple BIND_OPCODE_DONE in lazy binding info... |
| | 2079 | }, |
| | 2080 | macho.BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM => { |
| | 2081 | var next = try reader.readByte(); |
| | 2082 | while (next != @as(u8, 0)) { |
| | 2083 | next = try reader.readByte(); |
| | 2084 | } |
| | 2085 | }, |
| | 2086 | macho.BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB => { |
| | 2087 | const uleb_enc = try std.leb.readULEB128(u64, reader); |
| | 2088 | }, |
| | 2089 | macho.BIND_OPCODE_SET_DYLIB_SPECIAL_IMM => { |
| | 2090 | // We note the position in the stream to fixup later. |
| | 2091 | const pos = try reader.context.getPos(); |
| | 2092 | try fixups.append(pos - 1); |
| | 2093 | }, |
| | 2094 | else => {}, |
| | 2095 | } |
| | 2096 | } |
| | 2097 | assert(done); |
| | 2098 | |
| | 2099 | var buffer_dirty = false; |
| | 2100 | try stream.seekTo(0); |
| | 2101 | var writer = stream.writer(); |
| | 2102 | for (fixups.items) |pos| { |
| | 2103 | try writer.context.seekTo(pos); |
| | 2104 | const inst = macho.BIND_OPCODE_SET_DYLIB_ORDINAL_IMM | 1; |
| | 2105 | _ = try writer.write(&[_]u8{inst}); |
| | 2106 | buffer_dirty = true; |
| | 2107 | } |
| | 2108 | |
| | 2109 | return buffer_dirty; |
| | 2110 | } |