diff options
| author | Jakub Konka <kubkon@jakubkonka.com> | 2023-01-19 00:03:31 +0100 |
|---|---|---|
| committer | Jakub Konka <kubkon@jakubkonka.com> | 2023-01-19 00:05:45 +0100 |
| commit | 61e38d0c34337b63a2174ff89cc5d34c06eecb22 (patch) | |
| tree | 2ce2db8d27d8d85b97b4e7a4178c48173bffb563 /src/link/MachO.zig | |
| parent | 72c09b7b3b2b804bcc57befb5d82c7e89b6deeac (diff) | |
| download | zig-61e38d0c34337b63a2174ff89cc5d34c06eecb22.tar.gz zig-61e38d0c34337b63a2174ff89cc5d34c06eecb22.zip | |
macho+zld: add improved dyld opcodes emitters
Diffstat (limited to 'src/link/MachO.zig')
| -rw-r--r-- | src/link/MachO.zig | 210 |
1 files changed, 59 insertions, 151 deletions
diff --git a/src/link/MachO.zig b/src/link/MachO.zig index be770574b8..97e87a45b6 100644 --- a/src/link/MachO.zig +++ b/src/link/MachO.zig @@ -14,7 +14,6 @@ const mem = std.mem; const meta = std.meta; const aarch64 = @import("../arch/aarch64/bits.zig"); -const bind = @import("MachO/bind.zig"); const codegen = @import("../codegen.zig"); const dead_strip = @import("MachO/dead_strip.zig"); const fat = @import("MachO/fat.zig"); @@ -50,6 +49,10 @@ const Value = @import("../value.zig").Value; pub const DebugSymbols = @import("MachO/DebugSymbols.zig"); +const Bind = @import("MachO/dyld_info/bind.zig").Bind(*const MachO, MachO.SymbolWithLoc); +const LazyBind = @import("MachO/dyld_info/bind.zig").LazyBind(*const MachO, MachO.SymbolWithLoc); +const Rebase = @import("MachO/dyld_info/Rebase.zig"); + pub const base_tag: File.Tag = File.Tag.macho; pub const SearchStrategy = enum { @@ -3192,32 +3195,14 @@ fn writeLinkeditSegmentData(self: *MachO) !void { seg.vmsize = mem.alignForwardGeneric(u64, seg.filesize, self.page_size); } -const AtomLessThanByAddressContext = struct { - macho_file: *MachO, -}; - -fn atomLessThanByAddress(ctx: AtomLessThanByAddressContext, lhs: *Atom, rhs: *Atom) bool { - return lhs.getSymbol(ctx.macho_file).n_value < rhs.getSymbol(ctx.macho_file).n_value; -} - -fn collectRebaseData(self: *MachO, pointers: *std.ArrayList(bind.Pointer)) !void { +fn collectRebaseData(self: *MachO, rebase: *Rebase) !void { const gpa = self.base.allocator; - - var sorted_atoms_by_address = std.ArrayList(*Atom).init(gpa); - defer sorted_atoms_by_address.deinit(); - try sorted_atoms_by_address.ensureTotalCapacityPrecise(self.rebases.count()); - + const slice = self.sections.slice(); var it = self.rebases.keyIterator(); - while (it.next()) |key_ptr| { - sorted_atoms_by_address.appendAssumeCapacity(key_ptr.*); - } - std.sort.sort(*Atom, sorted_atoms_by_address.items, AtomLessThanByAddressContext{ - .macho_file = self, - }, atomLessThanByAddress); + while (it.next()) |key_ptr| { + const atom = key_ptr.*; - const slice = self.sections.slice(); - for (sorted_atoms_by_address.items) |atom| { log.debug(" ATOM(%{d}, '{s}')", .{ atom.sym_index, atom.getName(self) }); const sym = atom.getSymbol(self); @@ -3227,36 +3212,29 @@ fn collectRebaseData(self: *MachO, pointers: *std.ArrayList(bind.Pointer)) !void const base_offset = sym.n_value - seg.vmaddr; const rebases = self.rebases.get(atom).?; - try pointers.ensureUnusedCapacity(rebases.items.len); + try rebase.entries.ensureUnusedCapacity(gpa, rebases.items.len); + for (rebases.items) |offset| { log.debug(" | rebase at {x}", .{base_offset + offset}); - pointers.appendAssumeCapacity(.{ + rebase.entries.appendAssumeCapacity(.{ .offset = base_offset + offset, .segment_id = segment_index, }); } } + + try rebase.finalize(gpa); } -fn collectBindData(self: *MachO, pointers: *std.ArrayList(bind.Pointer), raw_bindings: anytype) !void { +fn collectBindData(self: *MachO, bind: anytype, raw_bindings: anytype) !void { const gpa = self.base.allocator; - - var sorted_atoms_by_address = std.ArrayList(*Atom).init(gpa); - defer sorted_atoms_by_address.deinit(); - try sorted_atoms_by_address.ensureTotalCapacityPrecise(raw_bindings.count()); - + const slice = self.sections.slice(); var it = raw_bindings.keyIterator(); - while (it.next()) |key_ptr| { - sorted_atoms_by_address.appendAssumeCapacity(key_ptr.*); - } - std.sort.sort(*Atom, sorted_atoms_by_address.items, AtomLessThanByAddressContext{ - .macho_file = self, - }, atomLessThanByAddress); + while (it.next()) |key_ptr| { + const atom = key_ptr.*; - const slice = self.sections.slice(); - for (sorted_atoms_by_address.items) |atom| { log.debug(" ATOM(%{d}, '{s}')", .{ atom.sym_index, atom.getName(self) }); const sym = atom.getSymbol(self); @@ -3266,7 +3244,8 @@ fn collectBindData(self: *MachO, pointers: *std.ArrayList(bind.Pointer), raw_bin const base_offset = sym.n_value - seg.vmaddr; const bindings = raw_bindings.get(atom).?; - try pointers.ensureUnusedCapacity(bindings.items.len); + try bind.entries.ensureUnusedCapacity(gpa, bindings.items.len); + for (bindings.items) |binding| { const bind_sym = self.getSymbol(binding.target); const bind_sym_name = self.getSymbolName(binding.target); @@ -3274,7 +3253,6 @@ fn collectBindData(self: *MachO, pointers: *std.ArrayList(bind.Pointer), raw_bin @bitCast(i16, bind_sym.n_desc), macho.N_SYMBOL_RESOLVER, ); - var flags: u4 = 0; log.debug(" | bind at {x}, import('{s}') in dylib({d})", .{ binding.offset + base_offset, bind_sym_name, @@ -3282,17 +3260,17 @@ fn collectBindData(self: *MachO, pointers: *std.ArrayList(bind.Pointer), raw_bin }); if (bind_sym.weakRef()) { log.debug(" | marking as weak ref ", .{}); - flags |= @truncate(u4, macho.BIND_SYMBOL_FLAGS_WEAK_IMPORT); } - pointers.appendAssumeCapacity(.{ + bind.entries.appendAssumeCapacity(.{ + .target = binding.target, .offset = binding.offset + base_offset, .segment_id = segment_index, - .dylib_ordinal = dylib_ordinal, - .name = bind_sym_name, - .bind_flags = flags, + .addend = 0, }); } } + + try bind.finalize(gpa, self); } fn collectExportData(self: *MachO, trie: *Trie) !void { @@ -3345,17 +3323,17 @@ fn writeDyldInfoData(self: *MachO) !void { const gpa = self.base.allocator; - var rebase_pointers = std.ArrayList(bind.Pointer).init(gpa); - defer rebase_pointers.deinit(); - try self.collectRebaseData(&rebase_pointers); + var rebase = Rebase{}; + defer rebase.deinit(gpa); + try self.collectRebaseData(&rebase); - var bind_pointers = std.ArrayList(bind.Pointer).init(gpa); - defer bind_pointers.deinit(); - try self.collectBindData(&bind_pointers, self.bindings); + var bind = Bind{}; + defer bind.deinit(gpa); + try self.collectBindData(&bind, self.bindings); - var lazy_bind_pointers = std.ArrayList(bind.Pointer).init(gpa); - defer lazy_bind_pointers.deinit(); - try self.collectBindData(&lazy_bind_pointers, self.lazy_bindings); + var lazy_bind = LazyBind{}; + defer lazy_bind.deinit(gpa); + try self.collectBindData(&lazy_bind, self.lazy_bindings); var trie: Trie = .{}; defer trie.deinit(gpa); @@ -3364,17 +3342,17 @@ fn writeDyldInfoData(self: *MachO) !void { const link_seg = self.getLinkeditSegmentPtr(); assert(mem.isAlignedGeneric(u64, link_seg.fileoff, @alignOf(u64))); const rebase_off = link_seg.fileoff; - const rebase_size = try bind.rebaseInfoSize(rebase_pointers.items); + const rebase_size = rebase.size(); const rebase_size_aligned = mem.alignForwardGeneric(u64, rebase_size, @alignOf(u64)); log.debug("writing rebase info from 0x{x} to 0x{x}", .{ rebase_off, rebase_off + rebase_size_aligned }); const bind_off = rebase_off + rebase_size_aligned; - const bind_size = try bind.bindInfoSize(bind_pointers.items); + const bind_size = bind.size(); const bind_size_aligned = mem.alignForwardGeneric(u64, bind_size, @alignOf(u64)); log.debug("writing bind info from 0x{x} to 0x{x}", .{ bind_off, bind_off + bind_size_aligned }); const lazy_bind_off = bind_off + bind_size_aligned; - const lazy_bind_size = try bind.lazyBindInfoSize(lazy_bind_pointers.items); + const lazy_bind_size = lazy_bind.size(); const lazy_bind_size_aligned = mem.alignForwardGeneric(u64, lazy_bind_size, @alignOf(u64)); log.debug("writing lazy bind info from 0x{x} to 0x{x}", .{ lazy_bind_off, @@ -3398,13 +3376,13 @@ fn writeDyldInfoData(self: *MachO) !void { var stream = std.io.fixedBufferStream(buffer); const writer = stream.writer(); - try bind.writeRebaseInfo(rebase_pointers.items, writer); + try rebase.write(writer); try stream.seekTo(bind_off - rebase_off); - try bind.writeBindInfo(bind_pointers.items, writer); + try bind.write(writer); try stream.seekTo(lazy_bind_off - rebase_off); - try bind.writeLazyBindInfo(lazy_bind_pointers.items, writer); + try lazy_bind.write(writer); try stream.seekTo(export_off - rebase_off); _ = try trie.write(writer); @@ -3415,9 +3393,7 @@ fn writeDyldInfoData(self: *MachO) !void { }); try self.base.file.?.pwriteAll(buffer, rebase_off); - const start = math.cast(usize, lazy_bind_off - rebase_off) orelse return error.Overflow; - const end = start + (math.cast(usize, lazy_bind_size) orelse return error.Overflow); - try self.populateLazyBindOffsetsInStubHelper(buffer[start..end]); + try self.populateLazyBindOffsetsInStubHelper(lazy_bind); self.dyld_info_cmd.rebase_off = @intCast(u32, rebase_off); self.dyld_info_cmd.rebase_size = @intCast(u32, rebase_size_aligned); @@ -3429,102 +3405,33 @@ fn writeDyldInfoData(self: *MachO) !void { self.dyld_info_cmd.export_size = @intCast(u32, export_size_aligned); } -fn populateLazyBindOffsetsInStubHelper(self: *MachO, buffer: []const u8) !void { - const gpa = self.base.allocator; +fn populateLazyBindOffsetsInStubHelper(self: *MachO, lazy_bind: LazyBind) !void { + if (lazy_bind.size() == 0) return; - const stub_helper_section_index = self.stub_helper_section_index orelse return; - if (self.stub_helper_preamble_atom == null) return; + const stub_helper_section_index = self.stub_helper_section_index.?; + assert(self.stub_helper_preamble_atom != null); const section = self.sections.get(stub_helper_section_index); - const last_atom = section.last_atom orelse return; - if (last_atom == self.stub_helper_preamble_atom.?) return; // TODO is this a redundant check? - - var table = std.AutoHashMap(i64, *Atom).init(gpa); - defer table.deinit(); - { - var stub_atom = last_atom; - var laptr_atom = self.sections.items(.last_atom)[self.la_symbol_ptr_section_index.?].?; - const base_addr = self.getSegment(self.la_symbol_ptr_section_index.?).vmaddr; - - while (true) { - const laptr_off = blk: { - const sym = laptr_atom.getSymbol(self); - break :blk @intCast(i64, sym.n_value - base_addr); - }; - try table.putNoClobber(laptr_off, stub_atom); - if (laptr_atom.prev) |prev| { - laptr_atom = prev; - stub_atom = stub_atom.prev.?; - } else break; - } - } - - var stream = std.io.fixedBufferStream(buffer); - var reader = stream.reader(); - var offsets = std.ArrayList(struct { sym_offset: i64, offset: u32 }).init(gpa); - try offsets.append(.{ .sym_offset = undefined, .offset = 0 }); - defer offsets.deinit(); - var valid_block = false; - - while (true) { - const inst = reader.readByte() catch |err| switch (err) { - error.EndOfStream => break, - }; - const opcode: u8 = inst & macho.BIND_OPCODE_MASK; - - switch (opcode) { - macho.BIND_OPCODE_DO_BIND => { - valid_block = true; - }, - macho.BIND_OPCODE_DONE => { - if (valid_block) { - const offset = try stream.getPos(); - try offsets.append(.{ .sym_offset = undefined, .offset = @intCast(u32, offset) }); - } - valid_block = false; - }, - 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 => { - var inserted = offsets.pop(); - inserted.sym_offset = try std.leb.readILEB128(i64, reader); - try offsets.append(inserted); - }, - macho.BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB => { - _ = try std.leb.readULEB128(u64, reader); - }, - macho.BIND_OPCODE_SET_ADDEND_SLEB => { - _ = try std.leb.readILEB128(i64, reader); - }, - else => {}, - } - } - - const header = self.sections.items(.header)[stub_helper_section_index]; const stub_offset: u4 = switch (self.base.options.target.cpu.arch) { .x86_64 => 1, .aarch64 => 2 * @sizeOf(u32), else => unreachable, }; - var buf: [@sizeOf(u32)]u8 = undefined; - _ = offsets.pop(); + const header = section.header; + var atom = section.last_atom.?; - while (offsets.popOrNull()) |bind_offset| { - const atom = table.get(bind_offset.sym_offset).?; + var index: usize = lazy_bind.offsets.items.len; + while (index > 0) : (index -= 1) { const sym = atom.getSymbol(self); const file_offset = header.offset + sym.n_value - header.addr + stub_offset; - mem.writeIntLittle(u32, &buf, bind_offset.offset); - log.debug("writing lazy bind offset in stub helper of 0x{x} for symbol {s} at offset 0x{x}", .{ - bind_offset.offset, - atom.getName(self), - file_offset, - }); - try self.base.file.?.pwriteAll(&buf, file_offset); + const bind_offset = lazy_bind.offsets.items[index - 1]; + + log.debug("writing lazy bind offset 0x{x} in stub helper at 0x{x}", .{ bind_offset, file_offset }); + + try self.base.file.?.pwriteAll(mem.asBytes(&bind_offset), file_offset); + + atom = atom.prev.?; } } @@ -3912,12 +3819,13 @@ pub fn getSymbolPtr(self: *MachO, sym_with_loc: SymbolWithLoc) *macho.nlist_64 { } /// Returns symbol described by `sym_with_loc` descriptor. -pub fn getSymbol(self: *MachO, sym_with_loc: SymbolWithLoc) macho.nlist_64 { - return self.getSymbolPtr(sym_with_loc).*; +pub fn getSymbol(self: *const MachO, sym_with_loc: SymbolWithLoc) macho.nlist_64 { + assert(sym_with_loc.file == null); + return self.locals.items[sym_with_loc.sym_index]; } /// Returns name of the symbol described by `sym_with_loc` descriptor. -pub fn getSymbolName(self: *MachO, sym_with_loc: SymbolWithLoc) []const u8 { +pub fn getSymbolName(self: *const MachO, sym_with_loc: SymbolWithLoc) []const u8 { assert(sym_with_loc.file == null); const sym = self.locals.items[sym_with_loc.sym_index]; return self.strtab.get(sym.n_strx).?; |
