| ... | ... | @@ -107,6 +107,7 @@ offset_table: std.ArrayListUnmanaged(u64) = .{}, |
| 107 | 107 | error_flags: File.ErrorFlags = File.ErrorFlags{}, |
| 108 | 108 | |
| 109 | 109 | cmd_table_dirty: bool = false, |
| 110 | other_dylibs_present: bool = false, |
| 110 | 111 | |
| 111 | 112 | /// A list of text blocks that have surplus capacity. This list can have false |
| 112 | 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 | 756 | const out_file = try directory.handle.openFile(self.base.options.emit.?.sub_path, .{ .write = true }); |
| 756 | 757 | try self.parseFromFile(out_file); |
| 757 | 758 | if (self.libsystem_cmd_index == null) { |
| 759 | if (self.other_dylibs_present) return; // TODO We cannot handle this situation yet. |
| 758 | 760 | const text_segment = self.load_commands.items[self.text_segment_cmd_index.?].Segment; |
| 759 | 761 | const text_section = text_segment.sections.items[self.text_section_index.?]; |
| 760 | 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 | 789 | mem.set(u8, dylib_cmd.data, 0); |
| 788 | 790 | mem.copy(u8, dylib_cmd.data, mem.spanZ(LIB_SYSTEM_PATH)); |
| 789 | 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 | 795 | // Write updated load commands and the header |
| 792 | 796 | try self.writeLoadCommands(); |
| 793 | 797 | try self.writeHeader(); |
| ... | ... | @@ -2002,6 +2006,8 @@ fn parseFromFile(self: *MachO, file: fs.File) !void { |
| 2002 | 2006 | const x = cmd.Dylib; |
| 2003 | 2007 | if (parseAndCmpName(x.data, mem.spanZ(LIB_SYSTEM_PATH))) { |
| 2004 | 2008 | self.libsystem_cmd_index = i; |
| 2009 | } else { |
| 2010 | self.other_dylibs_present = true; |
| 2005 | 2011 | } |
| 2006 | 2012 | }, |
| 2007 | 2013 | macho.LC_FUNCTION_STARTS => { |
| ... | ... | @@ -2030,3 +2036,75 @@ fn parseAndCmpName(name: []const u8, needle: []const u8) bool { |
| 2030 | 2036 | const len = mem.indexOfScalar(u8, name[0..], @as(u8, 0)) orelse name.len; |
| 2031 | 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 | } |