diff options
| author | Jakub Konka <kubkon@jakubkonka.com> | 2020-12-05 12:59:28 +0100 |
|---|---|---|
| committer | Jakub Konka <kubkon@jakubkonka.com> | 2020-12-17 10:04:53 +0100 |
| commit | 2e7883c59726a0832c3af6581fd96bf69a0fa3a6 (patch) | |
| tree | 0e798ee09923af2d8ea1be3982ff8af1c1ef6701 | |
| parent | eb528a9cbc4baebe16dda686bdc55d0feee82087 (diff) | |
| download | zig-2e7883c59726a0832c3af6581fd96bf69a0fa3a6.tar.gz zig-2e7883c59726a0832c3af6581fd96bf69a0fa3a6.zip | |
lld+macho: lld xcomp to x86_64 macos now works
| -rw-r--r-- | lib/std/macho.zig | 27 | ||||
| -rw-r--r-- | src/link/MachO.zig | 80 |
2 files changed, 106 insertions, 1 deletions
diff --git a/lib/std/macho.zig b/lib/std/macho.zig index ec0d23cd92..016590e36b 100644 --- a/lib/std/macho.zig +++ b/lib/std/macho.zig @@ -1257,6 +1257,33 @@ pub const VM_PROT_WRITE: vm_prot_t = 0x2; /// VM execute permission pub const VM_PROT_EXECUTE: vm_prot_t = 0x4; +pub const BIND_TYPE_POINTER: u8 = 1; +pub const BIND_TYPE_TEXT_ABSOLUTE32: u8 = 2; +pub const BIND_TYPE_TEXT_PCREL32: u8 = 3; + +pub const BIND_SPECIAL_DYLIB_SELF: i8 = 0; +pub const BIND_SPECIAL_DYLIB_MAIN_EXECUTABLE: i8 = -1; +pub const BIND_SPECIAL_DYLIB_FLAT_LOOKUP: i8 = -2; + +pub const BIND_SYMBOL_FLAGS_WEAK_IMPORT: u8 = 0x1; +pub const BIND_SYMBOL_FLAGS_NON_WEAK_DEFINITION: u8 = 0x8; + +pub const BIND_OPCODE_MASK: u8 = 0xf0; +pub const BIND_IMMEDIATE_MASK: u8 = 0x0f; +pub const BIND_OPCODE_DONE: u8 = 0x00; +pub const BIND_OPCODE_SET_DYLIB_ORDINAL_IMM: u8 = 0x10; +pub const BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB: u8 = 0x20; +pub const BIND_OPCODE_SET_DYLIB_SPECIAL_IMM: u8 = 0x30; +pub const BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM: u8 = 0x40; +pub const BIND_OPCODE_SET_TYPE_IMM: u8 = 0x50; +pub const BIND_OPCODE_SET_ADDEND_SLEB: u8 = 0x60; +pub const BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB: u8 = 0x70; +pub const BIND_OPCODE_ADD_ADDR_ULEB: 0x80; +pub const BIND_OPCODE_DO_BIND: u8 = 0x90; +pub const BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB: u8 = 0xa0; +pub const BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED: u8 = 0xb0; +pub const BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB: u8 = xc0; + pub const reloc_type_x86_64 = packed enum(u4) { /// for absolute addresses X86_64_RELOC_UNSIGNED = 0, diff --git a/src/link/MachO.zig b/src/link/MachO.zig index 69379b3f5e..8f7305f9f6 100644 --- a/src/link/MachO.zig +++ b/src/link/MachO.zig @@ -107,6 +107,7 @@ offset_table: std.ArrayListUnmanaged(u64) = .{}, error_flags: File.ErrorFlags = File.ErrorFlags{}, cmd_table_dirty: bool = false, +other_dylibs_present: bool = false, /// A list of text blocks that have surplus capacity. This list can have false /// positives, as functions grow and shrink over time, only sometimes being added @@ -755,6 +756,7 @@ fn linkWithLLD(self: *MachO, comp: *Compilation) !void { const out_file = try directory.handle.openFile(self.base.options.emit.?.sub_path, .{ .write = true }); try self.parseFromFile(out_file); if (self.libsystem_cmd_index == null) { + if (self.other_dylibs_present) return; // TODO We cannot handle this situation yet. const text_segment = self.load_commands.items[self.text_segment_cmd_index.?].Segment; const text_section = text_segment.sections.items[self.text_section_index.?]; const after_last_cmd_offset = self.header.?.sizeofcmds + @sizeOf(macho.mach_header_64); @@ -787,7 +789,9 @@ fn linkWithLLD(self: *MachO, comp: *Compilation) !void { mem.set(u8, dylib_cmd.data, 0); mem.copy(u8, dylib_cmd.data, mem.spanZ(LIB_SYSTEM_PATH)); try self.load_commands.append(self.base.allocator, .{ .Dylib = dylib_cmd }); - // TODO Fixup linkedit data + // Parse dyld info + try self.parseBindingInfo(); + try self.parseLazyBindingInfo(); // Write updated load commands and the header try self.writeLoadCommands(); try self.writeHeader(); @@ -2002,6 +2006,8 @@ fn parseFromFile(self: *MachO, file: fs.File) !void { const x = cmd.Dylib; if (parseAndCmpName(x.data, mem.spanZ(LIB_SYSTEM_PATH))) { self.libsystem_cmd_index = i; + } else { + self.other_dylibs_present = true; } }, macho.LC_FUNCTION_STARTS => { @@ -2030,3 +2036,75 @@ fn parseAndCmpName(name: []const u8, needle: []const u8) bool { const len = mem.indexOfScalar(u8, name[0..], @as(u8, 0)) orelse name.len; return mem.eql(u8, name[0..len], needle); } + +fn parseBindingInfo(self: *MachO) !void { + const dyld_info = self.load_commands.items[self.dyld_info_cmd_index.?].DyldInfoOnly; + var buffer = try self.base.allocator.alloc(u8, dyld_info.bind_size); + defer self.base.allocator.free(buffer); + const nread = try self.base.file.?.preadAll(buffer, dyld_info.bind_off); + assert(nread == buffer.len); + if (try parseAndFixupBindingInfoBuffer(self.base.allocator, buffer)) { + try self.base.file.?.pwriteAll(buffer, dyld_info.bind_off); + } +} + +fn parseLazyBindingInfo(self: *MachO) !void { + const dyld_info = self.load_commands.items[self.dyld_info_cmd_index.?].DyldInfoOnly; + var buffer = try self.base.allocator.alloc(u8, dyld_info.lazy_bind_size); + defer self.base.allocator.free(buffer); + const nread = try self.base.file.?.preadAll(buffer, dyld_info.lazy_bind_off); + assert(nread == buffer.len); + if (try parseAndFixupBindingInfoBuffer(self.base.allocator, buffer)) { + try self.base.file.?.pwriteAll(buffer, dyld_info.lazy_bind_off); + } +} + +fn parseAndFixupBindingInfoBuffer(allocator: *Allocator, buffer: []u8) !bool{ + var stream = std.io.fixedBufferStream(buffer); + var reader = stream.reader(); + var done = false; + var fixups = std.ArrayList(usize).init(allocator); + defer fixups.deinit(); + + while (true) { + const inst = reader.readByte() catch |err| switch (err) { + error.EndOfStream => break, + else => return err, + }; + const imm: u8 = inst & macho.BIND_IMMEDIATE_MASK; + const opcode: u8 = inst & macho.BIND_OPCODE_MASK; + switch (opcode) { + macho.BIND_OPCODE_DONE => { + done = true; // TODO There appear to be multiple BIND_OPCODE_DONE in lazy binding info... + }, + macho.BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM => { + var next = try reader.readByte(); + while (next != @as(u8, 0)) { + next = try reader.readByte(); + } + }, + macho.BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB => { + const uleb_enc = try std.leb.readULEB128(u64, reader); + }, + macho.BIND_OPCODE_SET_DYLIB_SPECIAL_IMM => { + // We note the position in the stream to fixup later. + const pos = try reader.context.getPos(); + try fixups.append(pos - 1); + }, + else => {}, + } + } + assert(done); + + var buffer_dirty = false; + try stream.seekTo(0); + var writer = stream.writer(); + for (fixups.items) |pos| { + try writer.context.seekTo(pos); + const inst = macho.BIND_OPCODE_SET_DYLIB_ORDINAL_IMM | 1; + _ = try writer.write(&[_]u8{inst}); + buffer_dirty = true; + } + + return buffer_dirty; +} |
