diff options
| author | Jakub Konka <kubkon@jakubkonka.com> | 2021-07-18 18:33:23 +0200 |
|---|---|---|
| committer | Jakub Konka <kubkon@jakubkonka.com> | 2021-07-18 18:36:48 +0200 |
| commit | 4bc72c48b76eb59e6f1eb160d3d590606e7ae090 (patch) | |
| tree | 24b330bd7a2128caecc787a937c130454f28a440 /src | |
| parent | f6d13e9d6f7a4b9161f369544501ecbb447c1658 (diff) | |
| download | zig-4bc72c48b76eb59e6f1eb160d3d590606e7ae090.tar.gz zig-4bc72c48b76eb59e6f1eb160d3d590606e7ae090.zip | |
macho: temporarily dupe a few linkedit fns so that traditional linker works
Diffstat (limited to 'src')
| -rw-r--r-- | src/link/MachO.zig | 267 |
1 files changed, 261 insertions, 6 deletions
diff --git a/src/link/MachO.zig b/src/link/MachO.zig index 66a1ee4e04..8aac8820da 100644 --- a/src/link/MachO.zig +++ b/src/link/MachO.zig @@ -583,7 +583,6 @@ fn linkWithZld(self: *MachO, comp: *Compilation) !void { const is_lib = self.base.options.output_mode == .Lib; const is_dyn_lib = self.base.options.link_mode == .Dynamic and is_lib; const is_exe_or_dyn_lib = is_dyn_lib or self.base.options.output_mode == .Exe; - const target = self.base.options.target; const stack_size = self.base.options.stack_size_override orelse 0; const allow_shlib_undefined = self.base.options.allow_shlib_undefined orelse !self.base.options.is_native_os; @@ -2755,6 +2754,7 @@ fn addRpaths(self: *MachO, rpaths: []const []const u8) !void { } fn flushZld(self: *MachO) !void { + self.load_commands_dirty = true; try self.writeTextBlocks(); try self.writeStubHelperCommon(); @@ -2778,10 +2778,10 @@ fn flushZld(self: *MachO) !void { try self.writeGotEntries(); try self.setEntryPoint(); - try self.writeRebaseInfoTable(); - try self.writeBindInfoTable(); - try self.writeLazyBindInfoTable(); - try self.writeExportInfo(); + try self.writeRebaseInfoTableZld(); + try self.writeBindInfoTableZld(); + try self.writeLazyBindInfoTableZld(); + try self.writeExportInfoZld(); try self.writeDices(); { @@ -2791,7 +2791,7 @@ fn flushZld(self: *MachO) !void { } try self.writeSymbolTable(); - try self.writeStringTable(); + try self.writeStringTableZld(); { // Seal __LINKEDIT size @@ -2856,6 +2856,244 @@ fn setEntryPoint(self: *MachO) !void { ec.stacksize = self.base.options.stack_size_override orelse 0; } +fn writeRebaseInfoTableZld(self: *MachO) !void { + var pointers = std.ArrayList(bind.Pointer).init(self.base.allocator); + defer pointers.deinit(); + + { + var it = self.blocks.iterator(); + while (it.next()) |entry| { + const match = entry.key_ptr.*; + var block: *TextBlock = entry.value_ptr.*; + + if (match.seg == self.text_segment_cmd_index.?) continue; // __TEXT is non-writable + + const seg = self.load_commands.items[match.seg].Segment; + + while (true) { + const sym = self.locals.items[block.local_sym_index]; + const base_offset = sym.n_value - seg.inner.vmaddr; + + for (block.rebases.items) |offset| { + try pointers.append(.{ + .offset = base_offset + offset, + .segment_id = match.seg, + }); + } + + if (block.prev) |prev| { + block = prev; + } else break; + } + } + } + + if (self.got_section_index) |idx| { + const seg = self.load_commands.items[self.data_const_segment_cmd_index.?].Segment; + const sect = seg.sections.items[idx]; + const base_offset = sect.addr - seg.inner.vmaddr; + const segment_id = @intCast(u16, self.data_const_segment_cmd_index.?); + + for (self.got_entries.items) |entry, i| { + if (entry.where == .import) continue; + + try pointers.append(.{ + .offset = base_offset + i * @sizeOf(u64), + .segment_id = segment_id, + }); + } + } + + if (self.la_symbol_ptr_section_index) |idx| { + const seg = self.load_commands.items[self.data_segment_cmd_index.?].Segment; + const sect = seg.sections.items[idx]; + const base_offset = sect.addr - seg.inner.vmaddr; + const segment_id = @intCast(u16, self.data_segment_cmd_index.?); + + try pointers.ensureUnusedCapacity(self.stubs.items.len); + for (self.stubs.items) |_, i| { + pointers.appendAssumeCapacity(.{ + .offset = base_offset + i * @sizeOf(u64), + .segment_id = segment_id, + }); + } + } + + std.sort.sort(bind.Pointer, pointers.items, {}, bind.pointerCmp); + + const size = try bind.rebaseInfoSize(pointers.items); + var buffer = try self.base.allocator.alloc(u8, @intCast(usize, size)); + defer self.base.allocator.free(buffer); + + var stream = std.io.fixedBufferStream(buffer); + try bind.writeRebaseInfo(pointers.items, stream.writer()); + + const seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment; + const dyld_info = &self.load_commands.items[self.dyld_info_cmd_index.?].DyldInfoOnly; + dyld_info.rebase_off = @intCast(u32, seg.inner.fileoff); + dyld_info.rebase_size = @intCast(u32, mem.alignForwardGeneric(u64, buffer.len, @sizeOf(u64))); + seg.inner.filesize += dyld_info.rebase_size; + + log.debug("writing rebase info from 0x{x} to 0x{x}", .{ dyld_info.rebase_off, dyld_info.rebase_off + dyld_info.rebase_size }); + + try self.base.file.?.pwriteAll(buffer, dyld_info.rebase_off); +} + +fn writeBindInfoTableZld(self: *MachO) !void { + var pointers = std.ArrayList(bind.Pointer).init(self.base.allocator); + defer pointers.deinit(); + + if (self.got_section_index) |idx| { + const seg = self.load_commands.items[self.data_const_segment_cmd_index.?].Segment; + const sect = seg.sections.items[idx]; + const base_offset = sect.addr - seg.inner.vmaddr; + const segment_id = @intCast(u16, self.data_const_segment_cmd_index.?); + + for (self.got_entries.items) |entry, i| { + if (entry.where == .local) continue; + + const sym = self.imports.items[entry.where_index]; + try pointers.append(.{ + .offset = base_offset + i * @sizeOf(u64), + .segment_id = segment_id, + .dylib_ordinal = unpackDylibOrdinal(sym.n_desc), + .name = self.getString(sym.n_strx), + }); + } + } + + { + var it = self.blocks.iterator(); + while (it.next()) |entry| { + const match = entry.key_ptr.*; + var block: *TextBlock = entry.value_ptr.*; + + if (match.seg == self.text_segment_cmd_index.?) continue; // __TEXT is non-writable + + const seg = self.load_commands.items[match.seg].Segment; + + while (true) { + const sym = self.locals.items[block.local_sym_index]; + const base_offset = sym.n_value - seg.inner.vmaddr; + + for (block.bindings.items) |binding| { + const bind_sym = self.imports.items[binding.local_sym_index]; + try pointers.append(.{ + .offset = binding.offset + base_offset, + .segment_id = match.seg, + .dylib_ordinal = unpackDylibOrdinal(bind_sym.n_desc), + .name = self.getString(bind_sym.n_strx), + }); + } + + if (block.prev) |prev| { + block = prev; + } else break; + } + } + } + + const size = try bind.bindInfoSize(pointers.items); + var buffer = try self.base.allocator.alloc(u8, @intCast(usize, size)); + defer self.base.allocator.free(buffer); + + var stream = std.io.fixedBufferStream(buffer); + try bind.writeBindInfo(pointers.items, stream.writer()); + + const seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment; + const dyld_info = &self.load_commands.items[self.dyld_info_cmd_index.?].DyldInfoOnly; + dyld_info.bind_off = @intCast(u32, seg.inner.fileoff + seg.inner.filesize); + dyld_info.bind_size = @intCast(u32, mem.alignForwardGeneric(u64, buffer.len, @alignOf(u64))); + seg.inner.filesize += dyld_info.bind_size; + + log.debug("writing binding info from 0x{x} to 0x{x}", .{ dyld_info.bind_off, dyld_info.bind_off + dyld_info.bind_size }); + + try self.base.file.?.pwriteAll(buffer, dyld_info.bind_off); +} + +fn writeLazyBindInfoTableZld(self: *MachO) !void { + var pointers = std.ArrayList(bind.Pointer).init(self.base.allocator); + defer pointers.deinit(); + + if (self.la_symbol_ptr_section_index) |idx| { + const seg = self.load_commands.items[self.data_segment_cmd_index.?].Segment; + const sect = seg.sections.items[idx]; + const base_offset = sect.addr - seg.inner.vmaddr; + const segment_id = @intCast(u16, self.data_segment_cmd_index.?); + + try pointers.ensureUnusedCapacity(self.stubs.items.len); + + for (self.stubs.items) |import_id, i| { + const sym = self.imports.items[import_id]; + pointers.appendAssumeCapacity(.{ + .offset = base_offset + i * @sizeOf(u64), + .segment_id = segment_id, + .dylib_ordinal = unpackDylibOrdinal(sym.n_desc), + .name = self.getString(sym.n_strx), + }); + } + } + + const size = try bind.lazyBindInfoSize(pointers.items); + var buffer = try self.base.allocator.alloc(u8, @intCast(usize, size)); + defer self.base.allocator.free(buffer); + + var stream = std.io.fixedBufferStream(buffer); + try bind.writeLazyBindInfo(pointers.items, stream.writer()); + + const seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment; + const dyld_info = &self.load_commands.items[self.dyld_info_cmd_index.?].DyldInfoOnly; + dyld_info.lazy_bind_off = @intCast(u32, seg.inner.fileoff + seg.inner.filesize); + dyld_info.lazy_bind_size = @intCast(u32, mem.alignForwardGeneric(u64, buffer.len, @alignOf(u64))); + seg.inner.filesize += dyld_info.lazy_bind_size; + + log.debug("writing lazy binding info from 0x{x} to 0x{x}", .{ dyld_info.lazy_bind_off, dyld_info.lazy_bind_off + dyld_info.lazy_bind_size }); + + try self.base.file.?.pwriteAll(buffer, dyld_info.lazy_bind_off); + try self.populateLazyBindOffsetsInStubHelper(buffer); +} + +fn writeExportInfoZld(self: *MachO) !void { + var trie = Trie.init(self.base.allocator); + defer trie.deinit(); + + const text_segment = self.load_commands.items[self.text_segment_cmd_index.?].Segment; + const base_address = text_segment.inner.vmaddr; + + // TODO handle macho.EXPORT_SYMBOL_FLAGS_REEXPORT and macho.EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER. + log.debug("writing export trie", .{}); + + for (self.globals.items) |sym| { + const sym_name = self.getString(sym.n_strx); + log.debug(" | putting '{s}' defined at 0x{x}", .{ sym_name, sym.n_value }); + + try trie.put(.{ + .name = sym_name, + .vmaddr_offset = sym.n_value - base_address, + .export_flags = macho.EXPORT_SYMBOL_FLAGS_KIND_REGULAR, + }); + } + + try trie.finalize(); + + var buffer = try self.base.allocator.alloc(u8, @intCast(usize, trie.size)); + defer self.base.allocator.free(buffer); + + var stream = std.io.fixedBufferStream(buffer); + const nwritten = try trie.write(stream.writer()); + assert(nwritten == trie.size); + + const seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment; + const dyld_info = &self.load_commands.items[self.dyld_info_cmd_index.?].DyldInfoOnly; + dyld_info.export_off = @intCast(u32, seg.inner.fileoff + seg.inner.filesize); + dyld_info.export_size = @intCast(u32, mem.alignForwardGeneric(u64, buffer.len, @alignOf(u64))); + seg.inner.filesize += dyld_info.export_size; + + log.debug("writing export info from 0x{x} to 0x{x}", .{ dyld_info.export_off, dyld_info.export_off + dyld_info.export_size }); + + try self.base.file.?.pwriteAll(buffer, dyld_info.export_off); +} + fn writeSymbolTable(self: *MachO) !void { const seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment; const symtab = &self.load_commands.items[self.symtab_cmd_index.?].Symtab; @@ -5222,6 +5460,23 @@ fn writeStringTable(self: *MachO) !void { self.strtab_dirty = false; } +fn writeStringTableZld(self: *MachO) !void { + const seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment; + const symtab = &self.load_commands.items[self.symtab_cmd_index.?].Symtab; + symtab.stroff = @intCast(u32, seg.inner.fileoff + seg.inner.filesize); + symtab.strsize = @intCast(u32, mem.alignForwardGeneric(u64, self.strtab.items.len, @alignOf(u64))); + seg.inner.filesize += symtab.strsize; + + log.debug("writing string table from 0x{x} to 0x{x}", .{ symtab.stroff, symtab.stroff + symtab.strsize }); + + try self.base.file.?.pwriteAll(self.strtab.items, symtab.stroff); + + if (symtab.strsize > self.strtab.items.len and self.base.options.target.cpu.arch == .x86_64) { + // This is the last section, so we need to pad it out. + try self.base.file.?.pwriteAll(&[_]u8{0}, seg.inner.fileoff + seg.inner.filesize - 1); + } +} + fn updateLinkeditSegmentSizes(self: *MachO) !void { if (!self.load_commands_dirty) return; |
