mirror of
https://github.com/ziglang/zig.git
synced 2026-01-30 03:03:46 +00:00
Merge pull request #14369 from ziglang/macho-dyld-ops
macho+zld: add improved dyld opcodes emitters
This commit is contained in:
commit
989b0e620b
@ -594,7 +594,8 @@ set(ZIG_STAGE2_SOURCES
|
||||
"${CMAKE_SOURCE_DIR}/src/link/MachO/Relocation.zig"
|
||||
"${CMAKE_SOURCE_DIR}/src/link/MachO/Trie.zig"
|
||||
"${CMAKE_SOURCE_DIR}/src/link/MachO/ZldAtom.zig"
|
||||
"${CMAKE_SOURCE_DIR}/src/link/MachO/bind.zig"
|
||||
"${CMAKE_SOURCE_DIR}/src/link/MachO/dyld_info/bind.zig"
|
||||
"${CMAKE_SOURCE_DIR}/src/link/MachO/dyld_info/Rebase.zig"
|
||||
"${CMAKE_SOURCE_DIR}/src/link/MachO/dead_strip.zig"
|
||||
"${CMAKE_SOURCE_DIR}/src/link/MachO/fat.zig"
|
||||
"${CMAKE_SOURCE_DIR}/src/link/MachO/load_commands.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());
|
||||
|
||||
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);
|
||||
|
||||
const slice = self.sections.slice();
|
||||
for (sorted_atoms_by_address.items) |atom| {
|
||||
var it = self.rebases.keyIterator();
|
||||
|
||||
while (it.next()) |key_ptr| {
|
||||
const atom = key_ptr.*;
|
||||
|
||||
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());
|
||||
|
||||
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);
|
||||
|
||||
const slice = self.sections.slice();
|
||||
for (sorted_atoms_by_address.items) |atom| {
|
||||
var it = raw_bindings.keyIterator();
|
||||
|
||||
while (it.next()) |key_ptr| {
|
||||
const atom = key_ptr.*;
|
||||
|
||||
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 = 0;
|
||||
while (index < lazy_bind.offsets.items.len) : (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];
|
||||
|
||||
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).?;
|
||||
|
||||
@ -1,138 +0,0 @@
|
||||
const std = @import("std");
|
||||
const leb = std.leb;
|
||||
const macho = std.macho;
|
||||
|
||||
pub const Pointer = struct {
|
||||
offset: u64,
|
||||
segment_id: u16,
|
||||
dylib_ordinal: ?i64 = null,
|
||||
name: ?[]const u8 = null,
|
||||
bind_flags: u4 = 0,
|
||||
};
|
||||
|
||||
pub fn rebaseInfoSize(pointers: []const Pointer) !u64 {
|
||||
var stream = std.io.countingWriter(std.io.null_writer);
|
||||
var writer = stream.writer();
|
||||
var size: u64 = 0;
|
||||
|
||||
for (pointers) |pointer| {
|
||||
size += 2;
|
||||
try leb.writeILEB128(writer, pointer.offset);
|
||||
size += 1;
|
||||
}
|
||||
|
||||
size += 1 + stream.bytes_written;
|
||||
return size;
|
||||
}
|
||||
|
||||
pub fn writeRebaseInfo(pointers: []const Pointer, writer: anytype) !void {
|
||||
for (pointers) |pointer| {
|
||||
try writer.writeByte(macho.REBASE_OPCODE_SET_TYPE_IMM | @truncate(u4, macho.REBASE_TYPE_POINTER));
|
||||
try writer.writeByte(macho.REBASE_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB | @truncate(u4, pointer.segment_id));
|
||||
|
||||
try leb.writeILEB128(writer, pointer.offset);
|
||||
try writer.writeByte(macho.REBASE_OPCODE_DO_REBASE_IMM_TIMES | @truncate(u4, 1));
|
||||
}
|
||||
try writer.writeByte(macho.REBASE_OPCODE_DONE);
|
||||
}
|
||||
|
||||
pub fn bindInfoSize(pointers: []const Pointer) !u64 {
|
||||
var stream = std.io.countingWriter(std.io.null_writer);
|
||||
var writer = stream.writer();
|
||||
var size: u64 = 0;
|
||||
|
||||
for (pointers) |pointer| {
|
||||
size += 1;
|
||||
if (pointer.dylib_ordinal.? > 15) {
|
||||
try leb.writeULEB128(writer, @bitCast(u64, pointer.dylib_ordinal.?));
|
||||
}
|
||||
size += 1;
|
||||
|
||||
size += 1;
|
||||
size += pointer.name.?.len;
|
||||
size += 1;
|
||||
|
||||
size += 1;
|
||||
|
||||
try leb.writeILEB128(writer, pointer.offset);
|
||||
size += 1;
|
||||
}
|
||||
|
||||
size += stream.bytes_written + 1;
|
||||
return size;
|
||||
}
|
||||
|
||||
pub fn writeBindInfo(pointers: []const Pointer, writer: anytype) !void {
|
||||
for (pointers) |pointer| {
|
||||
if (pointer.dylib_ordinal.? > 15) {
|
||||
try writer.writeByte(macho.BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB);
|
||||
try leb.writeULEB128(writer, @bitCast(u64, pointer.dylib_ordinal.?));
|
||||
} else if (pointer.dylib_ordinal.? > 0) {
|
||||
try writer.writeByte(macho.BIND_OPCODE_SET_DYLIB_ORDINAL_IMM | @truncate(u4, @bitCast(u64, pointer.dylib_ordinal.?)));
|
||||
} else {
|
||||
try writer.writeByte(macho.BIND_OPCODE_SET_DYLIB_SPECIAL_IMM | @truncate(u4, @bitCast(u64, pointer.dylib_ordinal.?)));
|
||||
}
|
||||
try writer.writeByte(macho.BIND_OPCODE_SET_TYPE_IMM | @truncate(u4, macho.BIND_TYPE_POINTER));
|
||||
|
||||
try writer.writeByte(macho.BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM | pointer.bind_flags);
|
||||
try writer.writeAll(pointer.name.?);
|
||||
try writer.writeByte(0);
|
||||
|
||||
try writer.writeByte(macho.BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB | @truncate(u4, pointer.segment_id));
|
||||
|
||||
try leb.writeILEB128(writer, pointer.offset);
|
||||
try writer.writeByte(macho.BIND_OPCODE_DO_BIND);
|
||||
}
|
||||
|
||||
try writer.writeByte(macho.BIND_OPCODE_DONE);
|
||||
}
|
||||
|
||||
pub fn lazyBindInfoSize(pointers: []const Pointer) !u64 {
|
||||
var stream = std.io.countingWriter(std.io.null_writer);
|
||||
var writer = stream.writer();
|
||||
var size: u64 = 0;
|
||||
|
||||
for (pointers) |pointer| {
|
||||
size += 1;
|
||||
|
||||
try leb.writeILEB128(writer, pointer.offset);
|
||||
|
||||
size += 1;
|
||||
if (pointer.dylib_ordinal.? > 15) {
|
||||
try leb.writeULEB128(writer, @bitCast(u64, pointer.dylib_ordinal.?));
|
||||
}
|
||||
|
||||
size += 1;
|
||||
size += pointer.name.?.len;
|
||||
size += 1;
|
||||
|
||||
size += 2;
|
||||
}
|
||||
|
||||
size += stream.bytes_written;
|
||||
return size;
|
||||
}
|
||||
|
||||
pub fn writeLazyBindInfo(pointers: []const Pointer, writer: anytype) !void {
|
||||
for (pointers) |pointer| {
|
||||
try writer.writeByte(macho.BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB | @truncate(u4, pointer.segment_id));
|
||||
|
||||
try leb.writeILEB128(writer, pointer.offset);
|
||||
|
||||
if (pointer.dylib_ordinal.? > 15) {
|
||||
try writer.writeByte(macho.BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB);
|
||||
try leb.writeULEB128(writer, @bitCast(u64, pointer.dylib_ordinal.?));
|
||||
} else if (pointer.dylib_ordinal.? > 0) {
|
||||
try writer.writeByte(macho.BIND_OPCODE_SET_DYLIB_ORDINAL_IMM | @truncate(u4, @bitCast(u64, pointer.dylib_ordinal.?)));
|
||||
} else {
|
||||
try writer.writeByte(macho.BIND_OPCODE_SET_DYLIB_SPECIAL_IMM | @truncate(u4, @bitCast(u64, pointer.dylib_ordinal.?)));
|
||||
}
|
||||
|
||||
try writer.writeByte(macho.BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM | pointer.bind_flags);
|
||||
try writer.writeAll(pointer.name.?);
|
||||
try writer.writeByte(0);
|
||||
|
||||
try writer.writeByte(macho.BIND_OPCODE_DO_BIND);
|
||||
try writer.writeByte(macho.BIND_OPCODE_DONE);
|
||||
}
|
||||
}
|
||||
574
src/link/MachO/dyld_info/Rebase.zig
Normal file
574
src/link/MachO/dyld_info/Rebase.zig
Normal file
@ -0,0 +1,574 @@
|
||||
const Rebase = @This();
|
||||
|
||||
const std = @import("std");
|
||||
const assert = std.debug.assert;
|
||||
const leb = std.leb;
|
||||
const log = std.log.scoped(.dyld_info);
|
||||
const macho = std.macho;
|
||||
const testing = std.testing;
|
||||
|
||||
const Allocator = std.mem.Allocator;
|
||||
|
||||
entries: std.ArrayListUnmanaged(Entry) = .{},
|
||||
buffer: std.ArrayListUnmanaged(u8) = .{},
|
||||
|
||||
const Entry = struct {
|
||||
offset: u64,
|
||||
segment_id: u8,
|
||||
|
||||
pub fn lessThan(ctx: void, entry: Entry, other: Entry) bool {
|
||||
_ = ctx;
|
||||
if (entry.segment_id == other.segment_id) {
|
||||
return entry.offset < other.offset;
|
||||
}
|
||||
return entry.segment_id < other.segment_id;
|
||||
}
|
||||
};
|
||||
|
||||
pub fn deinit(rebase: *Rebase, gpa: Allocator) void {
|
||||
rebase.entries.deinit(gpa);
|
||||
rebase.buffer.deinit(gpa);
|
||||
}
|
||||
|
||||
pub fn size(rebase: Rebase) u64 {
|
||||
return @intCast(u64, rebase.buffer.items.len);
|
||||
}
|
||||
|
||||
pub fn finalize(rebase: *Rebase, gpa: Allocator) !void {
|
||||
if (rebase.entries.items.len == 0) return;
|
||||
|
||||
const writer = rebase.buffer.writer(gpa);
|
||||
|
||||
std.sort.sort(Entry, rebase.entries.items, {}, Entry.lessThan);
|
||||
|
||||
try setTypePointer(writer);
|
||||
|
||||
var start: usize = 0;
|
||||
var seg_id: ?u8 = null;
|
||||
for (rebase.entries.items) |entry, i| {
|
||||
if (seg_id != null and seg_id.? == entry.segment_id) continue;
|
||||
try finalizeSegment(rebase.entries.items[start..i], writer);
|
||||
seg_id = entry.segment_id;
|
||||
start = i;
|
||||
}
|
||||
|
||||
try finalizeSegment(rebase.entries.items[start..], writer);
|
||||
try done(writer);
|
||||
}
|
||||
|
||||
fn finalizeSegment(entries: []const Entry, writer: anytype) !void {
|
||||
if (entries.len == 0) return;
|
||||
|
||||
const segment_id = entries[0].segment_id;
|
||||
var offset = entries[0].offset;
|
||||
try setSegmentOffset(segment_id, offset, writer);
|
||||
|
||||
var count: usize = 0;
|
||||
var skip: u64 = 0;
|
||||
var state: enum {
|
||||
start,
|
||||
times,
|
||||
times_skip,
|
||||
} = .times;
|
||||
|
||||
var i: usize = 0;
|
||||
while (i < entries.len) : (i += 1) {
|
||||
log.debug("{x}, {d}, {x}, {s}", .{ offset, count, skip, @tagName(state) });
|
||||
const current_offset = entries[i].offset;
|
||||
log.debug(" => {x}", .{current_offset});
|
||||
switch (state) {
|
||||
.start => {
|
||||
if (offset < current_offset) {
|
||||
const delta = current_offset - offset;
|
||||
try addAddr(delta, writer);
|
||||
offset += delta;
|
||||
}
|
||||
state = .times;
|
||||
offset += @sizeOf(u64);
|
||||
count = 1;
|
||||
},
|
||||
.times => {
|
||||
const delta = current_offset - offset;
|
||||
if (delta == 0) {
|
||||
count += 1;
|
||||
offset += @sizeOf(u64);
|
||||
continue;
|
||||
}
|
||||
if (count == 1) {
|
||||
state = .times_skip;
|
||||
skip = delta;
|
||||
offset += skip;
|
||||
i -= 1;
|
||||
} else {
|
||||
try rebaseTimes(count, writer);
|
||||
state = .start;
|
||||
i -= 1;
|
||||
}
|
||||
},
|
||||
.times_skip => {
|
||||
if (current_offset < offset) {
|
||||
count -= 1;
|
||||
if (count == 1) {
|
||||
try rebaseAddAddr(skip, writer);
|
||||
} else {
|
||||
try rebaseTimesSkip(count, skip, writer);
|
||||
}
|
||||
state = .start;
|
||||
offset = offset - (@sizeOf(u64) + skip);
|
||||
i -= 2;
|
||||
continue;
|
||||
}
|
||||
|
||||
const delta = current_offset - offset;
|
||||
if (delta == 0) {
|
||||
count += 1;
|
||||
offset += @sizeOf(u64) + skip;
|
||||
} else {
|
||||
try rebaseTimesSkip(count, skip, writer);
|
||||
state = .start;
|
||||
i -= 1;
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
switch (state) {
|
||||
.start => unreachable,
|
||||
.times => {
|
||||
try rebaseTimes(count, writer);
|
||||
},
|
||||
.times_skip => {
|
||||
try rebaseTimesSkip(count, skip, writer);
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn setTypePointer(writer: anytype) !void {
|
||||
log.debug(">>> set type: {d}", .{macho.REBASE_TYPE_POINTER});
|
||||
try writer.writeByte(macho.REBASE_OPCODE_SET_TYPE_IMM | @truncate(u4, macho.REBASE_TYPE_POINTER));
|
||||
}
|
||||
|
||||
fn setSegmentOffset(segment_id: u8, offset: u64, writer: anytype) !void {
|
||||
log.debug(">>> set segment: {d} and offset: {x}", .{ segment_id, offset });
|
||||
try writer.writeByte(macho.REBASE_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB | @truncate(u4, segment_id));
|
||||
try std.leb.writeULEB128(writer, offset);
|
||||
}
|
||||
|
||||
fn rebaseAddAddr(addr: u64, writer: anytype) !void {
|
||||
log.debug(">>> rebase with add: {x}", .{addr});
|
||||
try writer.writeByte(macho.REBASE_OPCODE_DO_REBASE_ADD_ADDR_ULEB);
|
||||
try std.leb.writeULEB128(writer, addr);
|
||||
}
|
||||
|
||||
fn rebaseTimes(count: usize, writer: anytype) !void {
|
||||
log.debug(">>> rebase with count: {d}", .{count});
|
||||
if (count <= 0xf) {
|
||||
try writer.writeByte(macho.REBASE_OPCODE_DO_REBASE_IMM_TIMES | @truncate(u4, count));
|
||||
} else {
|
||||
try writer.writeByte(macho.REBASE_OPCODE_DO_REBASE_ULEB_TIMES);
|
||||
try std.leb.writeULEB128(writer, count);
|
||||
}
|
||||
}
|
||||
|
||||
fn rebaseTimesSkip(count: usize, skip: u64, writer: anytype) !void {
|
||||
log.debug(">>> rebase with count: {d} and skip: {x}", .{ count, skip });
|
||||
try writer.writeByte(macho.REBASE_OPCODE_DO_REBASE_ULEB_TIMES_SKIPPING_ULEB);
|
||||
try std.leb.writeULEB128(writer, count);
|
||||
try std.leb.writeULEB128(writer, skip);
|
||||
}
|
||||
|
||||
fn addAddr(addr: u64, writer: anytype) !void {
|
||||
log.debug(">>> add: {x}", .{addr});
|
||||
if (std.mem.isAlignedGeneric(u64, addr, @sizeOf(u64))) {
|
||||
const imm = @divExact(addr, @sizeOf(u64));
|
||||
if (imm <= 0xf) {
|
||||
try writer.writeByte(macho.REBASE_OPCODE_ADD_ADDR_IMM_SCALED | @truncate(u4, imm));
|
||||
return;
|
||||
}
|
||||
}
|
||||
try writer.writeByte(macho.REBASE_OPCODE_ADD_ADDR_ULEB);
|
||||
try std.leb.writeULEB128(writer, addr);
|
||||
}
|
||||
|
||||
fn done(writer: anytype) !void {
|
||||
log.debug(">>> done", .{});
|
||||
try writer.writeByte(macho.REBASE_OPCODE_DONE);
|
||||
}
|
||||
|
||||
pub fn write(rebase: Rebase, writer: anytype) !void {
|
||||
if (rebase.size() == 0) return;
|
||||
try writer.writeAll(rebase.buffer.items);
|
||||
}
|
||||
|
||||
test "rebase - no entries" {
|
||||
const gpa = testing.allocator;
|
||||
|
||||
var rebase = Rebase{};
|
||||
defer rebase.deinit(gpa);
|
||||
|
||||
try rebase.finalize(gpa);
|
||||
try testing.expectEqual(@as(u64, 0), rebase.size());
|
||||
}
|
||||
|
||||
test "rebase - single entry" {
|
||||
const gpa = testing.allocator;
|
||||
|
||||
var rebase = Rebase{};
|
||||
defer rebase.deinit(gpa);
|
||||
try rebase.entries.append(gpa, .{
|
||||
.segment_id = 1,
|
||||
.offset = 0x10,
|
||||
});
|
||||
try rebase.finalize(gpa);
|
||||
try testing.expectEqualSlices(u8, &[_]u8{
|
||||
macho.REBASE_OPCODE_SET_TYPE_IMM | macho.REBASE_TYPE_POINTER,
|
||||
macho.REBASE_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB | 1,
|
||||
0x10,
|
||||
macho.REBASE_OPCODE_DO_REBASE_IMM_TIMES | 1,
|
||||
macho.REBASE_OPCODE_DONE,
|
||||
}, rebase.buffer.items);
|
||||
}
|
||||
|
||||
test "rebase - emitTimes - IMM" {
|
||||
const gpa = testing.allocator;
|
||||
|
||||
var rebase = Rebase{};
|
||||
defer rebase.deinit(gpa);
|
||||
|
||||
var i: u64 = 0;
|
||||
while (i < 10) : (i += 1) {
|
||||
try rebase.entries.append(gpa, .{
|
||||
.segment_id = 1,
|
||||
.offset = i * @sizeOf(u64),
|
||||
});
|
||||
}
|
||||
|
||||
try rebase.finalize(gpa);
|
||||
|
||||
try testing.expectEqualSlices(u8, &[_]u8{
|
||||
macho.REBASE_OPCODE_SET_TYPE_IMM | macho.REBASE_TYPE_POINTER,
|
||||
macho.REBASE_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB | 1,
|
||||
0x0,
|
||||
macho.REBASE_OPCODE_DO_REBASE_IMM_TIMES | 10,
|
||||
macho.REBASE_OPCODE_DONE,
|
||||
}, rebase.buffer.items);
|
||||
}
|
||||
|
||||
test "rebase - emitTimes - ULEB" {
|
||||
const gpa = testing.allocator;
|
||||
|
||||
var rebase = Rebase{};
|
||||
defer rebase.deinit(gpa);
|
||||
|
||||
var i: u64 = 0;
|
||||
while (i < 100) : (i += 1) {
|
||||
try rebase.entries.append(gpa, .{
|
||||
.segment_id = 1,
|
||||
.offset = i * @sizeOf(u64),
|
||||
});
|
||||
}
|
||||
|
||||
try rebase.finalize(gpa);
|
||||
|
||||
try testing.expectEqualSlices(u8, &[_]u8{
|
||||
macho.REBASE_OPCODE_SET_TYPE_IMM | macho.REBASE_TYPE_POINTER,
|
||||
macho.REBASE_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB | 1,
|
||||
0x0,
|
||||
macho.REBASE_OPCODE_DO_REBASE_ULEB_TIMES,
|
||||
0x64,
|
||||
macho.REBASE_OPCODE_DONE,
|
||||
}, rebase.buffer.items);
|
||||
}
|
||||
|
||||
test "rebase - emitTimes followed by addAddr followed by emitTimes" {
|
||||
const gpa = testing.allocator;
|
||||
|
||||
var rebase = Rebase{};
|
||||
defer rebase.deinit(gpa);
|
||||
|
||||
var offset: u64 = 0;
|
||||
var i: u64 = 0;
|
||||
while (i < 15) : (i += 1) {
|
||||
try rebase.entries.append(gpa, .{
|
||||
.segment_id = 1,
|
||||
.offset = offset,
|
||||
});
|
||||
offset += @sizeOf(u64);
|
||||
}
|
||||
|
||||
offset += @sizeOf(u64);
|
||||
|
||||
try rebase.entries.append(gpa, .{
|
||||
.segment_id = 1,
|
||||
.offset = offset,
|
||||
});
|
||||
|
||||
try rebase.finalize(gpa);
|
||||
|
||||
try testing.expectEqualSlices(u8, &[_]u8{
|
||||
macho.REBASE_OPCODE_SET_TYPE_IMM | macho.REBASE_TYPE_POINTER,
|
||||
macho.REBASE_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB | 1,
|
||||
0x0,
|
||||
macho.REBASE_OPCODE_DO_REBASE_IMM_TIMES | 15,
|
||||
macho.REBASE_OPCODE_ADD_ADDR_IMM_SCALED | 1,
|
||||
macho.REBASE_OPCODE_DO_REBASE_IMM_TIMES | 1,
|
||||
macho.REBASE_OPCODE_DONE,
|
||||
}, rebase.buffer.items);
|
||||
}
|
||||
|
||||
test "rebase - emitTimesSkip" {
|
||||
const gpa = testing.allocator;
|
||||
|
||||
var rebase = Rebase{};
|
||||
defer rebase.deinit(gpa);
|
||||
|
||||
var offset: u64 = 0;
|
||||
var i: u64 = 0;
|
||||
while (i < 15) : (i += 1) {
|
||||
try rebase.entries.append(gpa, .{
|
||||
.segment_id = 1,
|
||||
.offset = offset,
|
||||
});
|
||||
offset += 2 * @sizeOf(u64);
|
||||
}
|
||||
|
||||
try rebase.finalize(gpa);
|
||||
|
||||
try testing.expectEqualSlices(u8, &[_]u8{
|
||||
macho.REBASE_OPCODE_SET_TYPE_IMM | macho.REBASE_TYPE_POINTER,
|
||||
macho.REBASE_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB | 1,
|
||||
0x0,
|
||||
macho.REBASE_OPCODE_DO_REBASE_ULEB_TIMES_SKIPPING_ULEB,
|
||||
0xf,
|
||||
0x8,
|
||||
macho.REBASE_OPCODE_DONE,
|
||||
}, rebase.buffer.items);
|
||||
}
|
||||
|
||||
test "rebase - complex" {
|
||||
const gpa = testing.allocator;
|
||||
|
||||
var rebase = Rebase{};
|
||||
defer rebase.deinit(gpa);
|
||||
|
||||
try rebase.entries.append(gpa, .{
|
||||
.segment_id = 1,
|
||||
.offset = 0,
|
||||
});
|
||||
try rebase.entries.append(gpa, .{
|
||||
.segment_id = 1,
|
||||
.offset = 0x10,
|
||||
});
|
||||
try rebase.entries.append(gpa, .{
|
||||
.segment_id = 1,
|
||||
.offset = 0x40,
|
||||
});
|
||||
try rebase.entries.append(gpa, .{
|
||||
.segment_id = 1,
|
||||
.offset = 0x48,
|
||||
});
|
||||
try rebase.entries.append(gpa, .{
|
||||
.segment_id = 1,
|
||||
.offset = 0x50,
|
||||
});
|
||||
try rebase.entries.append(gpa, .{
|
||||
.segment_id = 1,
|
||||
.offset = 0x58,
|
||||
});
|
||||
try rebase.entries.append(gpa, .{
|
||||
.segment_id = 1,
|
||||
.offset = 0x70,
|
||||
});
|
||||
try rebase.finalize(gpa);
|
||||
|
||||
try testing.expectEqualSlices(u8, &[_]u8{
|
||||
macho.REBASE_OPCODE_SET_TYPE_IMM | macho.REBASE_TYPE_POINTER,
|
||||
macho.REBASE_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB | 1,
|
||||
0x0,
|
||||
macho.REBASE_OPCODE_DO_REBASE_ULEB_TIMES_SKIPPING_ULEB,
|
||||
0x2,
|
||||
0x8,
|
||||
macho.REBASE_OPCODE_ADD_ADDR_IMM_SCALED | 4,
|
||||
macho.REBASE_OPCODE_DO_REBASE_IMM_TIMES | 4,
|
||||
macho.REBASE_OPCODE_ADD_ADDR_IMM_SCALED | 2,
|
||||
macho.REBASE_OPCODE_DO_REBASE_IMM_TIMES | 1,
|
||||
macho.REBASE_OPCODE_DONE,
|
||||
}, rebase.buffer.items);
|
||||
}
|
||||
|
||||
test "rebase - complex 2" {
|
||||
const gpa = testing.allocator;
|
||||
|
||||
var rebase = Rebase{};
|
||||
defer rebase.deinit(gpa);
|
||||
|
||||
try rebase.entries.append(gpa, .{
|
||||
.segment_id = 1,
|
||||
.offset = 0,
|
||||
});
|
||||
try rebase.entries.append(gpa, .{
|
||||
.segment_id = 1,
|
||||
.offset = 0x10,
|
||||
});
|
||||
try rebase.entries.append(gpa, .{
|
||||
.segment_id = 1,
|
||||
.offset = 0x28,
|
||||
});
|
||||
try rebase.entries.append(gpa, .{
|
||||
.segment_id = 1,
|
||||
.offset = 0x48,
|
||||
});
|
||||
try rebase.entries.append(gpa, .{
|
||||
.segment_id = 1,
|
||||
.offset = 0x78,
|
||||
});
|
||||
try rebase.entries.append(gpa, .{
|
||||
.segment_id = 1,
|
||||
.offset = 0xb8,
|
||||
});
|
||||
try rebase.entries.append(gpa, .{
|
||||
.segment_id = 2,
|
||||
.offset = 0x0,
|
||||
});
|
||||
try rebase.entries.append(gpa, .{
|
||||
.segment_id = 2,
|
||||
.offset = 0x8,
|
||||
});
|
||||
try rebase.entries.append(gpa, .{
|
||||
.segment_id = 2,
|
||||
.offset = 0x10,
|
||||
});
|
||||
try rebase.entries.append(gpa, .{
|
||||
.segment_id = 2,
|
||||
.offset = 0x18,
|
||||
});
|
||||
try rebase.entries.append(gpa, .{
|
||||
.segment_id = 3,
|
||||
.offset = 0x0,
|
||||
});
|
||||
try rebase.entries.append(gpa, .{
|
||||
.segment_id = 3,
|
||||
.offset = 0x20,
|
||||
});
|
||||
try rebase.entries.append(gpa, .{
|
||||
.segment_id = 3,
|
||||
.offset = 0x40,
|
||||
});
|
||||
try rebase.entries.append(gpa, .{
|
||||
.segment_id = 3,
|
||||
.offset = 0x60,
|
||||
});
|
||||
try rebase.entries.append(gpa, .{
|
||||
.segment_id = 3,
|
||||
.offset = 0x68,
|
||||
});
|
||||
try rebase.finalize(gpa);
|
||||
|
||||
try testing.expectEqualSlices(u8, &[_]u8{
|
||||
macho.REBASE_OPCODE_SET_TYPE_IMM | macho.REBASE_TYPE_POINTER,
|
||||
macho.REBASE_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB | 1,
|
||||
0x0,
|
||||
macho.REBASE_OPCODE_DO_REBASE_ULEB_TIMES_SKIPPING_ULEB,
|
||||
0x2,
|
||||
0x8,
|
||||
macho.REBASE_OPCODE_ADD_ADDR_IMM_SCALED | 1,
|
||||
macho.REBASE_OPCODE_DO_REBASE_ULEB_TIMES_SKIPPING_ULEB,
|
||||
0x2,
|
||||
0x18,
|
||||
macho.REBASE_OPCODE_ADD_ADDR_IMM_SCALED | 2,
|
||||
macho.REBASE_OPCODE_DO_REBASE_ULEB_TIMES_SKIPPING_ULEB,
|
||||
0x2,
|
||||
0x38,
|
||||
macho.REBASE_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB | 2,
|
||||
0x0,
|
||||
macho.REBASE_OPCODE_DO_REBASE_IMM_TIMES | 4,
|
||||
macho.REBASE_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB | 3,
|
||||
0x0,
|
||||
macho.REBASE_OPCODE_DO_REBASE_ULEB_TIMES_SKIPPING_ULEB,
|
||||
0x3,
|
||||
0x18,
|
||||
macho.REBASE_OPCODE_DO_REBASE_IMM_TIMES | 2,
|
||||
macho.REBASE_OPCODE_DONE,
|
||||
}, rebase.buffer.items);
|
||||
}
|
||||
|
||||
test "rebase - composite" {
|
||||
const gpa = testing.allocator;
|
||||
|
||||
var rebase = Rebase{};
|
||||
defer rebase.deinit(gpa);
|
||||
|
||||
try rebase.entries.append(gpa, .{
|
||||
.segment_id = 1,
|
||||
.offset = 0x8,
|
||||
});
|
||||
try rebase.entries.append(gpa, .{
|
||||
.segment_id = 1,
|
||||
.offset = 0x38,
|
||||
});
|
||||
try rebase.entries.append(gpa, .{
|
||||
.segment_id = 1,
|
||||
.offset = 0xa0,
|
||||
});
|
||||
try rebase.entries.append(gpa, .{
|
||||
.segment_id = 1,
|
||||
.offset = 0xa8,
|
||||
});
|
||||
try rebase.entries.append(gpa, .{
|
||||
.segment_id = 1,
|
||||
.offset = 0xb0,
|
||||
});
|
||||
try rebase.entries.append(gpa, .{
|
||||
.segment_id = 1,
|
||||
.offset = 0xc0,
|
||||
});
|
||||
try rebase.entries.append(gpa, .{
|
||||
.segment_id = 1,
|
||||
.offset = 0xc8,
|
||||
});
|
||||
try rebase.entries.append(gpa, .{
|
||||
.segment_id = 1,
|
||||
.offset = 0xd0,
|
||||
});
|
||||
try rebase.entries.append(gpa, .{
|
||||
.segment_id = 1,
|
||||
.offset = 0xd8,
|
||||
});
|
||||
try rebase.entries.append(gpa, .{
|
||||
.segment_id = 1,
|
||||
.offset = 0xe0,
|
||||
});
|
||||
try rebase.entries.append(gpa, .{
|
||||
.segment_id = 1,
|
||||
.offset = 0xe8,
|
||||
});
|
||||
try rebase.entries.append(gpa, .{
|
||||
.segment_id = 1,
|
||||
.offset = 0xf0,
|
||||
});
|
||||
try rebase.entries.append(gpa, .{
|
||||
.segment_id = 1,
|
||||
.offset = 0xf8,
|
||||
});
|
||||
try rebase.entries.append(gpa, .{
|
||||
.segment_id = 1,
|
||||
.offset = 0x108,
|
||||
});
|
||||
try rebase.finalize(gpa);
|
||||
|
||||
try testing.expectEqualSlices(u8, &[_]u8{
|
||||
macho.REBASE_OPCODE_SET_TYPE_IMM | macho.REBASE_TYPE_POINTER,
|
||||
macho.REBASE_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB | 1,
|
||||
0x8,
|
||||
macho.REBASE_OPCODE_DO_REBASE_ULEB_TIMES_SKIPPING_ULEB,
|
||||
0x2,
|
||||
0x28,
|
||||
macho.REBASE_OPCODE_ADD_ADDR_IMM_SCALED | 7,
|
||||
macho.REBASE_OPCODE_DO_REBASE_IMM_TIMES | 3,
|
||||
macho.REBASE_OPCODE_ADD_ADDR_IMM_SCALED | 1,
|
||||
macho.REBASE_OPCODE_DO_REBASE_IMM_TIMES | 8,
|
||||
macho.REBASE_OPCODE_ADD_ADDR_IMM_SCALED | 1,
|
||||
macho.REBASE_OPCODE_DO_REBASE_IMM_TIMES | 1,
|
||||
macho.REBASE_OPCODE_DONE,
|
||||
}, rebase.buffer.items);
|
||||
}
|
||||
740
src/link/MachO/dyld_info/bind.zig
Normal file
740
src/link/MachO/dyld_info/bind.zig
Normal file
@ -0,0 +1,740 @@
|
||||
const std = @import("std");
|
||||
const assert = std.debug.assert;
|
||||
const leb = std.leb;
|
||||
const log = std.log.scoped(.dyld_info);
|
||||
const macho = std.macho;
|
||||
const testing = std.testing;
|
||||
|
||||
const Allocator = std.mem.Allocator;
|
||||
|
||||
pub fn Bind(comptime Ctx: type, comptime Target: type) type {
|
||||
return struct {
|
||||
entries: std.ArrayListUnmanaged(Entry) = .{},
|
||||
buffer: std.ArrayListUnmanaged(u8) = .{},
|
||||
|
||||
const Self = @This();
|
||||
|
||||
const Entry = struct {
|
||||
target: Target,
|
||||
offset: u64,
|
||||
segment_id: u8,
|
||||
addend: i64,
|
||||
|
||||
pub fn lessThan(ctx: Ctx, entry: Entry, other: Entry) bool {
|
||||
if (entry.segment_id == other.segment_id) {
|
||||
if (entry.target.eql(other.target)) {
|
||||
return entry.offset < other.offset;
|
||||
}
|
||||
const entry_name = ctx.getSymbolName(entry.target);
|
||||
const other_name = ctx.getSymbolName(other.target);
|
||||
return std.mem.lessThan(u8, entry_name, other_name);
|
||||
}
|
||||
return entry.segment_id < other.segment_id;
|
||||
}
|
||||
};
|
||||
|
||||
pub fn deinit(self: *Self, gpa: Allocator) void {
|
||||
self.entries.deinit(gpa);
|
||||
self.buffer.deinit(gpa);
|
||||
}
|
||||
|
||||
pub fn size(self: Self) u64 {
|
||||
return @intCast(u64, self.buffer.items.len);
|
||||
}
|
||||
|
||||
pub fn finalize(self: *Self, gpa: Allocator, ctx: Ctx) !void {
|
||||
if (self.entries.items.len == 0) return;
|
||||
|
||||
const writer = self.buffer.writer(gpa);
|
||||
|
||||
std.sort.sort(Entry, self.entries.items, ctx, Entry.lessThan);
|
||||
|
||||
var start: usize = 0;
|
||||
var seg_id: ?u8 = null;
|
||||
for (self.entries.items) |entry, i| {
|
||||
if (seg_id != null and seg_id.? == entry.segment_id) continue;
|
||||
try finalizeSegment(self.entries.items[start..i], ctx, writer);
|
||||
seg_id = entry.segment_id;
|
||||
start = i;
|
||||
}
|
||||
|
||||
try finalizeSegment(self.entries.items[start..], ctx, writer);
|
||||
try done(writer);
|
||||
}
|
||||
|
||||
fn finalizeSegment(entries: []const Entry, ctx: Ctx, writer: anytype) !void {
|
||||
if (entries.len == 0) return;
|
||||
|
||||
const seg_id = entries[0].segment_id;
|
||||
try setSegmentOffset(seg_id, 0, writer);
|
||||
|
||||
var offset: u64 = 0;
|
||||
var addend: i64 = 0;
|
||||
var count: usize = 0;
|
||||
var skip: u64 = 0;
|
||||
var target: ?Target = null;
|
||||
|
||||
var state: enum {
|
||||
start,
|
||||
bind_single,
|
||||
bind_times_skip,
|
||||
} = .start;
|
||||
|
||||
var i: usize = 0;
|
||||
while (i < entries.len) : (i += 1) {
|
||||
const current = entries[i];
|
||||
if (target == null or !target.?.eql(current.target)) {
|
||||
switch (state) {
|
||||
.start => {},
|
||||
.bind_single => try doBind(writer),
|
||||
.bind_times_skip => try doBindTimesSkip(count, skip, writer),
|
||||
}
|
||||
state = .start;
|
||||
target = current.target;
|
||||
|
||||
const sym = ctx.getSymbol(current.target);
|
||||
const name = ctx.getSymbolName(current.target);
|
||||
const flags: u8 = if (sym.weakRef()) macho.BIND_SYMBOL_FLAGS_WEAK_IMPORT else 0;
|
||||
const ordinal = @divTrunc(@bitCast(i16, sym.n_desc), macho.N_SYMBOL_RESOLVER);
|
||||
|
||||
try setSymbol(name, flags, writer);
|
||||
try setTypePointer(writer);
|
||||
try setDylibOrdinal(ordinal, writer);
|
||||
|
||||
if (current.addend != addend) {
|
||||
addend = current.addend;
|
||||
try setAddend(addend, writer);
|
||||
}
|
||||
}
|
||||
|
||||
log.debug("{x}, {d}, {x}, {?x}, {s}", .{ offset, count, skip, addend, @tagName(state) });
|
||||
log.debug(" => {x}", .{current.offset});
|
||||
switch (state) {
|
||||
.start => {
|
||||
if (current.offset < offset) {
|
||||
try addAddr(@bitCast(u64, @intCast(i64, current.offset) - @intCast(i64, offset)), writer);
|
||||
offset = offset - (offset - current.offset);
|
||||
} else if (current.offset > offset) {
|
||||
const delta = current.offset - offset;
|
||||
try addAddr(delta, writer);
|
||||
offset += delta;
|
||||
}
|
||||
state = .bind_single;
|
||||
offset += @sizeOf(u64);
|
||||
count = 1;
|
||||
},
|
||||
.bind_single => {
|
||||
if (current.offset == offset) {
|
||||
try doBind(writer);
|
||||
state = .start;
|
||||
} else if (current.offset > offset) {
|
||||
const delta = current.offset - offset;
|
||||
state = .bind_times_skip;
|
||||
skip = @intCast(u64, delta);
|
||||
offset += skip;
|
||||
} else unreachable;
|
||||
i -= 1;
|
||||
},
|
||||
.bind_times_skip => {
|
||||
if (current.offset < offset) {
|
||||
count -= 1;
|
||||
if (count == 1) {
|
||||
try doBindAddAddr(skip, writer);
|
||||
} else {
|
||||
try doBindTimesSkip(count, skip, writer);
|
||||
}
|
||||
state = .start;
|
||||
offset = offset - (@sizeOf(u64) + skip);
|
||||
i -= 2;
|
||||
} else if (current.offset == offset) {
|
||||
count += 1;
|
||||
offset += @sizeOf(u64) + skip;
|
||||
} else {
|
||||
try doBindTimesSkip(count, skip, writer);
|
||||
state = .start;
|
||||
i -= 1;
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
switch (state) {
|
||||
.start => unreachable,
|
||||
.bind_single => try doBind(writer),
|
||||
.bind_times_skip => try doBindTimesSkip(count, skip, writer),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn write(self: Self, writer: anytype) !void {
|
||||
if (self.size() == 0) return;
|
||||
try writer.writeAll(self.buffer.items);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
pub fn LazyBind(comptime Ctx: type, comptime Target: type) type {
|
||||
return struct {
|
||||
entries: std.ArrayListUnmanaged(Entry) = .{},
|
||||
buffer: std.ArrayListUnmanaged(u8) = .{},
|
||||
offsets: std.ArrayListUnmanaged(u32) = .{},
|
||||
|
||||
const Self = @This();
|
||||
|
||||
const Entry = struct {
|
||||
target: Target,
|
||||
offset: u64,
|
||||
segment_id: u8,
|
||||
addend: i64,
|
||||
};
|
||||
|
||||
pub fn deinit(self: *Self, gpa: Allocator) void {
|
||||
self.entries.deinit(gpa);
|
||||
self.buffer.deinit(gpa);
|
||||
self.offsets.deinit(gpa);
|
||||
}
|
||||
|
||||
pub fn size(self: Self) u64 {
|
||||
return @intCast(u64, self.buffer.items.len);
|
||||
}
|
||||
|
||||
pub fn finalize(self: *Self, gpa: Allocator, ctx: Ctx) !void {
|
||||
if (self.entries.items.len == 0) return;
|
||||
|
||||
try self.offsets.ensureTotalCapacityPrecise(gpa, self.entries.items.len);
|
||||
|
||||
var cwriter = std.io.countingWriter(self.buffer.writer(gpa));
|
||||
const writer = cwriter.writer();
|
||||
|
||||
var addend: i64 = 0;
|
||||
|
||||
for (self.entries.items) |entry| {
|
||||
self.offsets.appendAssumeCapacity(@intCast(u32, cwriter.bytes_written));
|
||||
|
||||
const sym = ctx.getSymbol(entry.target);
|
||||
const name = ctx.getSymbolName(entry.target);
|
||||
const flags: u8 = if (sym.weakRef()) macho.BIND_SYMBOL_FLAGS_WEAK_IMPORT else 0;
|
||||
const ordinal = @divTrunc(@bitCast(i16, sym.n_desc), macho.N_SYMBOL_RESOLVER);
|
||||
|
||||
try setSegmentOffset(entry.segment_id, entry.offset, writer);
|
||||
try setSymbol(name, flags, writer);
|
||||
try setDylibOrdinal(ordinal, writer);
|
||||
|
||||
if (entry.addend != addend) {
|
||||
try setAddend(entry.addend, writer);
|
||||
addend = entry.addend;
|
||||
}
|
||||
|
||||
try doBind(writer);
|
||||
try done(writer);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn write(self: Self, writer: anytype) !void {
|
||||
if (self.size() == 0) return;
|
||||
try writer.writeAll(self.buffer.items);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
fn setSegmentOffset(segment_id: u8, offset: u64, writer: anytype) !void {
|
||||
log.debug(">>> set segment: {d} and offset: {x}", .{ segment_id, offset });
|
||||
try writer.writeByte(macho.BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB | @truncate(u4, segment_id));
|
||||
try std.leb.writeULEB128(writer, offset);
|
||||
}
|
||||
|
||||
fn setSymbol(name: []const u8, flags: u8, writer: anytype) !void {
|
||||
log.debug(">>> set symbol: {s} with flags: {x}", .{ name, flags });
|
||||
try writer.writeByte(macho.BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM | @truncate(u4, flags));
|
||||
try writer.writeAll(name);
|
||||
try writer.writeByte(0);
|
||||
}
|
||||
|
||||
fn setTypePointer(writer: anytype) !void {
|
||||
log.debug(">>> set type: {d}", .{macho.BIND_TYPE_POINTER});
|
||||
try writer.writeByte(macho.BIND_OPCODE_SET_TYPE_IMM | @truncate(u4, macho.BIND_TYPE_POINTER));
|
||||
}
|
||||
|
||||
fn setDylibOrdinal(ordinal: i16, writer: anytype) !void {
|
||||
if (ordinal <= 0) {
|
||||
switch (ordinal) {
|
||||
macho.BIND_SPECIAL_DYLIB_SELF,
|
||||
macho.BIND_SPECIAL_DYLIB_MAIN_EXECUTABLE,
|
||||
macho.BIND_SPECIAL_DYLIB_FLAT_LOOKUP,
|
||||
=> {},
|
||||
else => unreachable, // Invalid dylib special binding
|
||||
}
|
||||
log.debug(">>> set dylib special: {d}", .{ordinal});
|
||||
const cast = @bitCast(u16, ordinal);
|
||||
try writer.writeByte(macho.BIND_OPCODE_SET_DYLIB_SPECIAL_IMM | @truncate(u4, cast));
|
||||
} else {
|
||||
const cast = @bitCast(u16, ordinal);
|
||||
log.debug(">>> set dylib ordinal: {d}", .{ordinal});
|
||||
if (cast <= 0xf) {
|
||||
try writer.writeByte(macho.BIND_OPCODE_SET_DYLIB_ORDINAL_IMM | @truncate(u4, cast));
|
||||
} else {
|
||||
try writer.writeByte(macho.BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB);
|
||||
try std.leb.writeULEB128(writer, cast);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn setAddend(addend: i64, writer: anytype) !void {
|
||||
log.debug(">>> set addend: {x}", .{addend});
|
||||
try writer.writeByte(macho.BIND_OPCODE_SET_ADDEND_SLEB);
|
||||
try std.leb.writeILEB128(writer, addend);
|
||||
}
|
||||
|
||||
fn doBind(writer: anytype) !void {
|
||||
log.debug(">>> bind", .{});
|
||||
try writer.writeByte(macho.BIND_OPCODE_DO_BIND);
|
||||
}
|
||||
|
||||
fn doBindAddAddr(addr: u64, writer: anytype) !void {
|
||||
log.debug(">>> bind with add: {x}", .{addr});
|
||||
if (std.mem.isAlignedGeneric(u64, addr, @sizeOf(u64))) {
|
||||
const imm = @divExact(addr, @sizeOf(u64));
|
||||
if (imm <= 0xf) {
|
||||
try writer.writeByte(
|
||||
macho.BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED | @truncate(u4, imm),
|
||||
);
|
||||
return;
|
||||
}
|
||||
}
|
||||
try writer.writeByte(macho.BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB);
|
||||
try std.leb.writeULEB128(writer, addr);
|
||||
}
|
||||
|
||||
fn doBindTimesSkip(count: usize, skip: u64, writer: anytype) !void {
|
||||
log.debug(">>> bind with count: {d} and skip: {x}", .{ count, skip });
|
||||
try writer.writeByte(macho.BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB);
|
||||
try std.leb.writeULEB128(writer, count);
|
||||
try std.leb.writeULEB128(writer, skip);
|
||||
}
|
||||
|
||||
fn addAddr(addr: u64, writer: anytype) !void {
|
||||
log.debug(">>> add: {x}", .{addr});
|
||||
try writer.writeByte(macho.BIND_OPCODE_ADD_ADDR_ULEB);
|
||||
try std.leb.writeULEB128(writer, addr);
|
||||
}
|
||||
|
||||
fn done(writer: anytype) !void {
|
||||
log.debug(">>> done", .{});
|
||||
try writer.writeByte(macho.BIND_OPCODE_DONE);
|
||||
}
|
||||
|
||||
const TestContext = struct {
|
||||
symbols: std.ArrayListUnmanaged(macho.nlist_64) = .{},
|
||||
strtab: std.ArrayListUnmanaged(u8) = .{},
|
||||
|
||||
const Target = struct {
|
||||
index: u32,
|
||||
|
||||
fn eql(this: Target, other: Target) bool {
|
||||
return this.index == other.index;
|
||||
}
|
||||
};
|
||||
|
||||
fn deinit(ctx: *TestContext, gpa: Allocator) void {
|
||||
ctx.symbols.deinit(gpa);
|
||||
ctx.strtab.deinit(gpa);
|
||||
}
|
||||
|
||||
fn addSymbol(ctx: *TestContext, gpa: Allocator, name: []const u8, ordinal: i16, flags: u16) !void {
|
||||
const n_strx = try ctx.addString(gpa, name);
|
||||
var n_desc = @bitCast(u16, ordinal * macho.N_SYMBOL_RESOLVER);
|
||||
n_desc |= flags;
|
||||
try ctx.symbols.append(gpa, .{
|
||||
.n_value = 0,
|
||||
.n_strx = n_strx,
|
||||
.n_desc = n_desc,
|
||||
.n_type = macho.N_EXT,
|
||||
.n_sect = 0,
|
||||
});
|
||||
}
|
||||
|
||||
fn addString(ctx: *TestContext, gpa: Allocator, name: []const u8) !u32 {
|
||||
const n_strx = @intCast(u32, ctx.strtab.items.len);
|
||||
try ctx.strtab.appendSlice(gpa, name);
|
||||
try ctx.strtab.append(gpa, 0);
|
||||
return n_strx;
|
||||
}
|
||||
|
||||
fn getSymbol(ctx: TestContext, target: Target) macho.nlist_64 {
|
||||
return ctx.symbols.items[target.index];
|
||||
}
|
||||
|
||||
fn getSymbolName(ctx: TestContext, target: Target) []const u8 {
|
||||
const sym = ctx.getSymbol(target);
|
||||
assert(sym.n_strx < ctx.strtab.items.len);
|
||||
return std.mem.sliceTo(@ptrCast([*:0]const u8, ctx.strtab.items.ptr + sym.n_strx), 0);
|
||||
}
|
||||
};
|
||||
|
||||
fn generateTestContext() !TestContext {
|
||||
const gpa = testing.allocator;
|
||||
var ctx = TestContext{};
|
||||
try ctx.addSymbol(gpa, "_import_1", 1, 0);
|
||||
try ctx.addSymbol(gpa, "_import_2", 1, 0);
|
||||
try ctx.addSymbol(gpa, "_import_3", 1, 0);
|
||||
try ctx.addSymbol(gpa, "_import_4", 2, 0);
|
||||
try ctx.addSymbol(gpa, "_import_5_weak", 2, macho.N_WEAK_REF);
|
||||
try ctx.addSymbol(gpa, "_import_6", 2, 0);
|
||||
return ctx;
|
||||
}
|
||||
|
||||
test "bind - no entries" {
|
||||
const gpa = testing.allocator;
|
||||
|
||||
var test_context = try generateTestContext();
|
||||
defer test_context.deinit(gpa);
|
||||
|
||||
var bind = Bind(TestContext, TestContext.Target){};
|
||||
defer bind.deinit(gpa);
|
||||
|
||||
try bind.finalize(gpa, test_context);
|
||||
try testing.expectEqual(@as(u64, 0), bind.size());
|
||||
}
|
||||
|
||||
test "bind - single entry" {
|
||||
const gpa = testing.allocator;
|
||||
|
||||
var test_context = try generateTestContext();
|
||||
defer test_context.deinit(gpa);
|
||||
|
||||
var bind = Bind(TestContext, TestContext.Target){};
|
||||
defer bind.deinit(gpa);
|
||||
|
||||
try bind.entries.append(gpa, .{
|
||||
.offset = 0x10,
|
||||
.segment_id = 1,
|
||||
.target = TestContext.Target{ .index = 0 },
|
||||
.addend = 0,
|
||||
});
|
||||
try bind.finalize(gpa, test_context);
|
||||
try testing.expectEqualSlices(u8, &[_]u8{
|
||||
macho.BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB | 1,
|
||||
0x0,
|
||||
macho.BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM | 0,
|
||||
0x5f,
|
||||
0x69,
|
||||
0x6d,
|
||||
0x70,
|
||||
0x6f,
|
||||
0x72,
|
||||
0x74,
|
||||
0x5f,
|
||||
0x31,
|
||||
0x0,
|
||||
macho.BIND_OPCODE_SET_TYPE_IMM | 1,
|
||||
macho.BIND_OPCODE_SET_DYLIB_ORDINAL_IMM | 1,
|
||||
macho.BIND_OPCODE_ADD_ADDR_ULEB,
|
||||
0x10,
|
||||
macho.BIND_OPCODE_DO_BIND,
|
||||
macho.BIND_OPCODE_DONE,
|
||||
}, bind.buffer.items);
|
||||
}
|
||||
|
||||
test "bind - multiple occurrences within the same segment" {
|
||||
const gpa = testing.allocator;
|
||||
|
||||
var test_context = try generateTestContext();
|
||||
defer test_context.deinit(gpa);
|
||||
|
||||
var bind = Bind(TestContext, TestContext.Target){};
|
||||
defer bind.deinit(gpa);
|
||||
|
||||
try bind.entries.append(gpa, .{
|
||||
.offset = 0x10,
|
||||
.segment_id = 1,
|
||||
.target = TestContext.Target{ .index = 0 },
|
||||
.addend = 0,
|
||||
});
|
||||
try bind.entries.append(gpa, .{
|
||||
.offset = 0x18,
|
||||
.segment_id = 1,
|
||||
.target = TestContext.Target{ .index = 0 },
|
||||
.addend = 0,
|
||||
});
|
||||
try bind.entries.append(gpa, .{
|
||||
.offset = 0x20,
|
||||
.segment_id = 1,
|
||||
.target = TestContext.Target{ .index = 0 },
|
||||
.addend = 0,
|
||||
});
|
||||
try bind.entries.append(gpa, .{
|
||||
.offset = 0x28,
|
||||
.segment_id = 1,
|
||||
.target = TestContext.Target{ .index = 0 },
|
||||
.addend = 0,
|
||||
});
|
||||
|
||||
try bind.finalize(gpa, test_context);
|
||||
try testing.expectEqualSlices(u8, &[_]u8{
|
||||
macho.BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB | 1,
|
||||
0x0,
|
||||
macho.BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM | 0,
|
||||
0x5f,
|
||||
0x69,
|
||||
0x6d,
|
||||
0x70,
|
||||
0x6f,
|
||||
0x72,
|
||||
0x74,
|
||||
0x5f,
|
||||
0x31,
|
||||
0x0,
|
||||
macho.BIND_OPCODE_SET_TYPE_IMM | 1,
|
||||
macho.BIND_OPCODE_SET_DYLIB_ORDINAL_IMM | 1,
|
||||
macho.BIND_OPCODE_ADD_ADDR_ULEB,
|
||||
0x10,
|
||||
macho.BIND_OPCODE_DO_BIND,
|
||||
macho.BIND_OPCODE_DO_BIND,
|
||||
macho.BIND_OPCODE_DO_BIND,
|
||||
macho.BIND_OPCODE_DO_BIND,
|
||||
macho.BIND_OPCODE_DONE,
|
||||
}, bind.buffer.items);
|
||||
}
|
||||
|
||||
test "bind - multiple occurrences with skip and addend" {
|
||||
const gpa = testing.allocator;
|
||||
|
||||
var test_context = try generateTestContext();
|
||||
defer test_context.deinit(gpa);
|
||||
|
||||
var bind = Bind(TestContext, TestContext.Target){};
|
||||
defer bind.deinit(gpa);
|
||||
|
||||
try bind.entries.append(gpa, .{
|
||||
.offset = 0x0,
|
||||
.segment_id = 1,
|
||||
.target = TestContext.Target{ .index = 0 },
|
||||
.addend = 0x10,
|
||||
});
|
||||
try bind.entries.append(gpa, .{
|
||||
.offset = 0x10,
|
||||
.segment_id = 1,
|
||||
.target = TestContext.Target{ .index = 0 },
|
||||
.addend = 0x10,
|
||||
});
|
||||
try bind.entries.append(gpa, .{
|
||||
.offset = 0x20,
|
||||
.segment_id = 1,
|
||||
.target = TestContext.Target{ .index = 0 },
|
||||
.addend = 0x10,
|
||||
});
|
||||
try bind.entries.append(gpa, .{
|
||||
.offset = 0x30,
|
||||
.segment_id = 1,
|
||||
.target = TestContext.Target{ .index = 0 },
|
||||
.addend = 0x10,
|
||||
});
|
||||
|
||||
try bind.finalize(gpa, test_context);
|
||||
try testing.expectEqualSlices(u8, &[_]u8{
|
||||
macho.BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB | 1,
|
||||
0x0,
|
||||
macho.BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM | 0,
|
||||
0x5f,
|
||||
0x69,
|
||||
0x6d,
|
||||
0x70,
|
||||
0x6f,
|
||||
0x72,
|
||||
0x74,
|
||||
0x5f,
|
||||
0x31,
|
||||
0x0,
|
||||
macho.BIND_OPCODE_SET_TYPE_IMM | 1,
|
||||
macho.BIND_OPCODE_SET_DYLIB_ORDINAL_IMM | 1,
|
||||
macho.BIND_OPCODE_SET_ADDEND_SLEB,
|
||||
0x10,
|
||||
macho.BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB,
|
||||
0x4,
|
||||
0x8,
|
||||
macho.BIND_OPCODE_DONE,
|
||||
}, bind.buffer.items);
|
||||
}
|
||||
|
||||
test "bind - complex" {
|
||||
const gpa = testing.allocator;
|
||||
|
||||
var test_context = try generateTestContext();
|
||||
defer test_context.deinit(gpa);
|
||||
|
||||
var bind = Bind(TestContext, TestContext.Target){};
|
||||
defer bind.deinit(gpa);
|
||||
|
||||
try bind.entries.append(gpa, .{
|
||||
.offset = 0x58,
|
||||
.segment_id = 1,
|
||||
.target = TestContext.Target{ .index = 0 },
|
||||
.addend = 0,
|
||||
});
|
||||
try bind.entries.append(gpa, .{
|
||||
.offset = 0x100,
|
||||
.segment_id = 1,
|
||||
.target = TestContext.Target{ .index = 1 },
|
||||
.addend = 0x10,
|
||||
});
|
||||
try bind.entries.append(gpa, .{
|
||||
.offset = 0x110,
|
||||
.segment_id = 1,
|
||||
.target = TestContext.Target{ .index = 1 },
|
||||
.addend = 0x10,
|
||||
});
|
||||
try bind.entries.append(gpa, .{
|
||||
.offset = 0x130,
|
||||
.segment_id = 1,
|
||||
.target = TestContext.Target{ .index = 1 },
|
||||
.addend = 0x10,
|
||||
});
|
||||
try bind.entries.append(gpa, .{
|
||||
.offset = 0x140,
|
||||
.segment_id = 1,
|
||||
.target = TestContext.Target{ .index = 1 },
|
||||
.addend = 0x10,
|
||||
});
|
||||
try bind.entries.append(gpa, .{
|
||||
.offset = 0x148,
|
||||
.segment_id = 1,
|
||||
.target = TestContext.Target{ .index = 2 },
|
||||
.addend = 0,
|
||||
});
|
||||
|
||||
try bind.finalize(gpa, test_context);
|
||||
try testing.expectEqualSlices(u8, &[_]u8{
|
||||
macho.BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB | 1,
|
||||
0x0,
|
||||
macho.BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM | 0,
|
||||
0x5f,
|
||||
0x69,
|
||||
0x6d,
|
||||
0x70,
|
||||
0x6f,
|
||||
0x72,
|
||||
0x74,
|
||||
0x5f,
|
||||
0x31,
|
||||
0x0,
|
||||
macho.BIND_OPCODE_SET_TYPE_IMM | 1,
|
||||
macho.BIND_OPCODE_SET_DYLIB_ORDINAL_IMM | 1,
|
||||
macho.BIND_OPCODE_ADD_ADDR_ULEB,
|
||||
0x58,
|
||||
macho.BIND_OPCODE_DO_BIND,
|
||||
macho.BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM | 0,
|
||||
0x5f,
|
||||
0x69,
|
||||
0x6d,
|
||||
0x70,
|
||||
0x6f,
|
||||
0x72,
|
||||
0x74,
|
||||
0x5f,
|
||||
0x32,
|
||||
0x0,
|
||||
macho.BIND_OPCODE_SET_TYPE_IMM | 1,
|
||||
macho.BIND_OPCODE_SET_DYLIB_ORDINAL_IMM | 1,
|
||||
macho.BIND_OPCODE_SET_ADDEND_SLEB,
|
||||
0x10,
|
||||
macho.BIND_OPCODE_ADD_ADDR_ULEB,
|
||||
0xa0,
|
||||
0x1,
|
||||
macho.BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB,
|
||||
0x2,
|
||||
0x8,
|
||||
macho.BIND_OPCODE_ADD_ADDR_ULEB,
|
||||
0x10,
|
||||
macho.BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB,
|
||||
0x2,
|
||||
0x8,
|
||||
macho.BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM | 0,
|
||||
0x5f,
|
||||
0x69,
|
||||
0x6d,
|
||||
0x70,
|
||||
0x6f,
|
||||
0x72,
|
||||
0x74,
|
||||
0x5f,
|
||||
0x33,
|
||||
0x0,
|
||||
macho.BIND_OPCODE_SET_TYPE_IMM | 1,
|
||||
macho.BIND_OPCODE_SET_DYLIB_ORDINAL_IMM | 1,
|
||||
macho.BIND_OPCODE_SET_ADDEND_SLEB,
|
||||
0x0,
|
||||
macho.BIND_OPCODE_ADD_ADDR_ULEB,
|
||||
0xf8,
|
||||
0xff,
|
||||
0xff,
|
||||
0xff,
|
||||
0xff,
|
||||
0xff,
|
||||
0xff,
|
||||
0xff,
|
||||
0xff,
|
||||
0x1,
|
||||
macho.BIND_OPCODE_DO_BIND,
|
||||
macho.BIND_OPCODE_DONE,
|
||||
}, bind.buffer.items);
|
||||
}
|
||||
|
||||
test "lazy bind" {
|
||||
const gpa = testing.allocator;
|
||||
|
||||
var test_context = try generateTestContext();
|
||||
defer test_context.deinit(gpa);
|
||||
|
||||
var bind = LazyBind(TestContext, TestContext.Target){};
|
||||
defer bind.deinit(gpa);
|
||||
|
||||
try bind.entries.append(gpa, .{
|
||||
.offset = 0x10,
|
||||
.segment_id = 1,
|
||||
.target = TestContext.Target{ .index = 0 },
|
||||
.addend = 0,
|
||||
});
|
||||
try bind.entries.append(gpa, .{
|
||||
.offset = 0x20,
|
||||
.segment_id = 2,
|
||||
.target = TestContext.Target{ .index = 1 },
|
||||
.addend = 0x10,
|
||||
});
|
||||
|
||||
try bind.finalize(gpa, test_context);
|
||||
try testing.expectEqualSlices(u8, &[_]u8{
|
||||
macho.BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB | 1,
|
||||
0x10,
|
||||
macho.BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM | 0,
|
||||
0x5f,
|
||||
0x69,
|
||||
0x6d,
|
||||
0x70,
|
||||
0x6f,
|
||||
0x72,
|
||||
0x74,
|
||||
0x5f,
|
||||
0x31,
|
||||
0x0,
|
||||
macho.BIND_OPCODE_SET_DYLIB_ORDINAL_IMM | 1,
|
||||
macho.BIND_OPCODE_DO_BIND,
|
||||
macho.BIND_OPCODE_DONE,
|
||||
macho.BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB | 2,
|
||||
0x20,
|
||||
macho.BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM | 0,
|
||||
0x5f,
|
||||
0x69,
|
||||
0x6d,
|
||||
0x70,
|
||||
0x6f,
|
||||
0x72,
|
||||
0x74,
|
||||
0x5f,
|
||||
0x32,
|
||||
0x0,
|
||||
macho.BIND_OPCODE_SET_DYLIB_ORDINAL_IMM | 1,
|
||||
macho.BIND_OPCODE_SET_ADDEND_SLEB,
|
||||
0x10,
|
||||
macho.BIND_OPCODE_DO_BIND,
|
||||
macho.BIND_OPCODE_DONE,
|
||||
}, bind.buffer.items);
|
||||
}
|
||||
@ -9,7 +9,6 @@ const math = std.math;
|
||||
const mem = std.mem;
|
||||
|
||||
const aarch64 = @import("../../arch/aarch64/bits.zig");
|
||||
const bind = @import("bind.zig");
|
||||
const dead_strip = @import("dead_strip.zig");
|
||||
const fat = @import("fat.zig");
|
||||
const link = @import("../../link.zig");
|
||||
@ -32,6 +31,10 @@ const Object = @import("Object.zig");
|
||||
const StringTable = @import("../strtab.zig").StringTable;
|
||||
const Trie = @import("Trie.zig");
|
||||
|
||||
const Bind = @import("dyld_info/bind.zig").Bind(*const Zld, SymbolWithLoc);
|
||||
const LazyBind = @import("dyld_info/bind.zig").LazyBind(*const Zld, SymbolWithLoc);
|
||||
const Rebase = @import("dyld_info/Rebase.zig");
|
||||
|
||||
pub const Zld = struct {
|
||||
gpa: Allocator,
|
||||
file: fs.File,
|
||||
@ -1778,14 +1781,14 @@ pub const Zld = struct {
|
||||
fn collectRebaseDataFromContainer(
|
||||
self: *Zld,
|
||||
sect_id: u8,
|
||||
pointers: *std.ArrayList(bind.Pointer),
|
||||
rebase: *Rebase,
|
||||
container: anytype,
|
||||
) !void {
|
||||
const slice = self.sections.slice();
|
||||
const segment_index = slice.items(.segment_index)[sect_id];
|
||||
const seg = self.getSegment(sect_id);
|
||||
|
||||
try pointers.ensureUnusedCapacity(container.items.len);
|
||||
try rebase.entries.ensureUnusedCapacity(self.gpa, container.items.len);
|
||||
|
||||
for (container.items) |entry| {
|
||||
const target_sym = entry.getTargetSymbol(self);
|
||||
@ -1796,19 +1799,19 @@ pub const Zld = struct {
|
||||
|
||||
log.debug(" | rebase at {x}", .{base_offset});
|
||||
|
||||
pointers.appendAssumeCapacity(.{
|
||||
rebase.entries.appendAssumeCapacity(.{
|
||||
.offset = base_offset,
|
||||
.segment_id = segment_index,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
fn collectRebaseData(self: *Zld, pointers: *std.ArrayList(bind.Pointer)) !void {
|
||||
fn collectRebaseData(self: *Zld, rebase: *Rebase, reverse_lookups: [][]u32) !void {
|
||||
log.debug("collecting rebase data", .{});
|
||||
|
||||
// First, unpack GOT entries
|
||||
if (self.getSectionByName("__DATA_CONST", "__got")) |sect_id| {
|
||||
try self.collectRebaseDataFromContainer(sect_id, pointers, self.got_entries);
|
||||
try self.collectRebaseDataFromContainer(sect_id, rebase, self.got_entries);
|
||||
}
|
||||
|
||||
const slice = self.sections.slice();
|
||||
@ -1820,7 +1823,7 @@ pub const Zld = struct {
|
||||
const seg = self.getSegment(sect_id);
|
||||
var atom_index = slice.items(.first_atom_index)[sect_id];
|
||||
|
||||
try pointers.ensureUnusedCapacity(self.stubs.items.len);
|
||||
try rebase.entries.ensureUnusedCapacity(self.gpa, self.stubs.items.len);
|
||||
|
||||
while (true) {
|
||||
const atom = self.getAtom(atom_index);
|
||||
@ -1829,7 +1832,7 @@ pub const Zld = struct {
|
||||
|
||||
log.debug(" | rebase at {x}", .{base_offset});
|
||||
|
||||
pointers.appendAssumeCapacity(.{
|
||||
rebase.entries.appendAssumeCapacity(.{
|
||||
.offset = base_offset,
|
||||
.segment_id = segment_index,
|
||||
});
|
||||
@ -1896,13 +1899,16 @@ pub const Zld = struct {
|
||||
},
|
||||
else => unreachable,
|
||||
}
|
||||
const target = Atom.parseRelocTarget(self, atom_index, rel, reverse_lookups[atom.getFile().?]);
|
||||
const target_sym = self.getSymbol(target);
|
||||
if (target_sym.undf()) continue;
|
||||
|
||||
const base_offset = @intCast(i32, sym.n_value - segment.vmaddr);
|
||||
const rel_offset = rel.r_address - base_rel_offset;
|
||||
const offset = @intCast(u64, base_offset + rel_offset);
|
||||
log.debug(" | rebase at {x}", .{offset});
|
||||
|
||||
try pointers.append(.{
|
||||
try rebase.entries.append(self.gpa, .{
|
||||
.offset = offset,
|
||||
.segment_id = segment_index,
|
||||
});
|
||||
@ -1914,19 +1920,21 @@ pub const Zld = struct {
|
||||
} else break;
|
||||
}
|
||||
}
|
||||
|
||||
try rebase.finalize(self.gpa);
|
||||
}
|
||||
|
||||
fn collectBindDataFromContainer(
|
||||
self: *Zld,
|
||||
sect_id: u8,
|
||||
pointers: *std.ArrayList(bind.Pointer),
|
||||
bind: *Bind,
|
||||
container: anytype,
|
||||
) !void {
|
||||
const slice = self.sections.slice();
|
||||
const segment_index = slice.items(.segment_index)[sect_id];
|
||||
const seg = self.getSegment(sect_id);
|
||||
|
||||
try pointers.ensureUnusedCapacity(container.items.len);
|
||||
try bind.entries.ensureUnusedCapacity(self.gpa, container.items.len);
|
||||
|
||||
for (container.items) |entry| {
|
||||
const bind_sym_name = entry.getTargetSymbolName(self);
|
||||
@ -1937,7 +1945,6 @@ pub const Zld = struct {
|
||||
const base_offset = sym.n_value - seg.vmaddr;
|
||||
|
||||
const dylib_ordinal = @divTrunc(@bitCast(i16, bind_sym.n_desc), macho.N_SYMBOL_RESOLVER);
|
||||
var flags: u4 = 0;
|
||||
log.debug(" | bind at {x}, import('{s}') in dylib({d})", .{
|
||||
base_offset,
|
||||
bind_sym_name,
|
||||
@ -1945,29 +1952,27 @@ pub const Zld = struct {
|
||||
});
|
||||
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 = entry.target,
|
||||
.offset = base_offset,
|
||||
.segment_id = segment_index,
|
||||
.dylib_ordinal = dylib_ordinal,
|
||||
.name = bind_sym_name,
|
||||
.bind_flags = flags,
|
||||
.addend = 0,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
fn collectBindData(self: *Zld, pointers: *std.ArrayList(bind.Pointer), reverse_lookups: [][]u32) !void {
|
||||
fn collectBindData(self: *Zld, bind: *Bind, reverse_lookups: [][]u32) !void {
|
||||
log.debug("collecting bind data", .{});
|
||||
|
||||
// First, unpack GOT section
|
||||
if (self.getSectionByName("__DATA_CONST", "__got")) |sect_id| {
|
||||
try self.collectBindDataFromContainer(sect_id, pointers, self.got_entries);
|
||||
try self.collectBindDataFromContainer(sect_id, bind, self.got_entries);
|
||||
}
|
||||
|
||||
// Next, unpack TLV pointers section
|
||||
if (self.getSectionByName("__DATA", "__thread_ptrs")) |sect_id| {
|
||||
try self.collectBindDataFromContainer(sect_id, pointers, self.tlv_ptr_entries);
|
||||
try self.collectBindDataFromContainer(sect_id, bind, self.tlv_ptr_entries);
|
||||
}
|
||||
|
||||
// Finally, unpack the rest.
|
||||
@ -2033,27 +2038,27 @@ pub const Zld = struct {
|
||||
const bind_sym = self.getSymbol(global);
|
||||
if (!bind_sym.undf()) continue;
|
||||
|
||||
const base_offset = @intCast(i32, sym.n_value - segment.vmaddr);
|
||||
const rel_offset = rel.r_address - base_rel_offset;
|
||||
const base_offset = sym.n_value - segment.vmaddr;
|
||||
const rel_offset = @intCast(u32, rel.r_address - base_rel_offset);
|
||||
const offset = @intCast(u64, base_offset + rel_offset);
|
||||
const code = Atom.getAtomCode(self, atom_index);
|
||||
const addend = mem.readIntLittle(i64, code[rel_offset..][0..8]);
|
||||
|
||||
const dylib_ordinal = @divTrunc(@bitCast(i16, bind_sym.n_desc), macho.N_SYMBOL_RESOLVER);
|
||||
var flags: u4 = 0;
|
||||
log.debug(" | bind at {x}, import('{s}') in dylib({d})", .{
|
||||
base_offset,
|
||||
bind_sym_name,
|
||||
dylib_ordinal,
|
||||
});
|
||||
log.debug(" | with addend {x}", .{addend});
|
||||
if (bind_sym.weakRef()) {
|
||||
log.debug(" | marking as weak ref ", .{});
|
||||
flags |= @truncate(u4, macho.BIND_SYMBOL_FLAGS_WEAK_IMPORT);
|
||||
}
|
||||
try pointers.append(.{
|
||||
try bind.entries.append(self.gpa, .{
|
||||
.target = global,
|
||||
.offset = offset,
|
||||
.segment_id = segment_index,
|
||||
.dylib_ordinal = dylib_ordinal,
|
||||
.name = bind_sym_name,
|
||||
.bind_flags = flags,
|
||||
.addend = addend,
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -2062,9 +2067,11 @@ pub const Zld = struct {
|
||||
} else break;
|
||||
}
|
||||
}
|
||||
|
||||
try bind.finalize(self.gpa, self);
|
||||
}
|
||||
|
||||
fn collectLazyBindData(self: *Zld, pointers: *std.ArrayList(bind.Pointer)) !void {
|
||||
fn collectLazyBindData(self: *Zld, lazy_bind: *LazyBind) !void {
|
||||
const sect_id = self.getSectionByName("__DATA", "__la_symbol_ptr") orelse return;
|
||||
|
||||
log.debug("collecting lazy bind data", .{});
|
||||
@ -2075,7 +2082,7 @@ pub const Zld = struct {
|
||||
var atom_index = slice.items(.first_atom_index)[sect_id];
|
||||
|
||||
// TODO: we actually don't need to store lazy pointer atoms as they are synthetically generated by the linker
|
||||
try pointers.ensureUnusedCapacity(self.stubs.items.len);
|
||||
try lazy_bind.entries.ensureUnusedCapacity(self.gpa, self.stubs.items.len);
|
||||
|
||||
var count: u32 = 0;
|
||||
while (true) : (count += 1) {
|
||||
@ -2090,7 +2097,6 @@ pub const Zld = struct {
|
||||
const bind_sym = stub_entry.getTargetSymbol(self);
|
||||
const bind_sym_name = stub_entry.getTargetSymbolName(self);
|
||||
const dylib_ordinal = @divTrunc(@bitCast(i16, bind_sym.n_desc), macho.N_SYMBOL_RESOLVER);
|
||||
var flags: u4 = 0;
|
||||
log.debug(" | lazy bind at {x}, import('{s}') in dylib({d})", .{
|
||||
base_offset,
|
||||
bind_sym_name,
|
||||
@ -2098,20 +2104,20 @@ pub const Zld = struct {
|
||||
});
|
||||
if (bind_sym.weakRef()) {
|
||||
log.debug(" | marking as weak ref ", .{});
|
||||
flags |= @truncate(u4, macho.BIND_SYMBOL_FLAGS_WEAK_IMPORT);
|
||||
}
|
||||
pointers.appendAssumeCapacity(.{
|
||||
lazy_bind.entries.appendAssumeCapacity(.{
|
||||
.target = stub_entry.target,
|
||||
.offset = base_offset,
|
||||
.segment_id = segment_index,
|
||||
.dylib_ordinal = dylib_ordinal,
|
||||
.name = bind_sym_name,
|
||||
.bind_flags = flags,
|
||||
.addend = 0,
|
||||
});
|
||||
|
||||
if (atom.next_index) |next_index| {
|
||||
atom_index = next_index;
|
||||
} else break;
|
||||
}
|
||||
|
||||
try lazy_bind.finalize(self.gpa, self);
|
||||
}
|
||||
|
||||
fn collectExportData(self: *Zld, trie: *Trie) !void {
|
||||
@ -2161,17 +2167,17 @@ pub const Zld = struct {
|
||||
fn writeDyldInfoData(self: *Zld, reverse_lookups: [][]u32) !void {
|
||||
const gpa = self.gpa;
|
||||
|
||||
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, reverse_lookups);
|
||||
|
||||
var bind_pointers = std.ArrayList(bind.Pointer).init(gpa);
|
||||
defer bind_pointers.deinit();
|
||||
try self.collectBindData(&bind_pointers, reverse_lookups);
|
||||
var bind = Bind{};
|
||||
defer bind.deinit(gpa);
|
||||
try self.collectBindData(&bind, reverse_lookups);
|
||||
|
||||
var lazy_bind_pointers = std.ArrayList(bind.Pointer).init(gpa);
|
||||
defer lazy_bind_pointers.deinit();
|
||||
try self.collectLazyBindData(&lazy_bind_pointers);
|
||||
var lazy_bind = LazyBind{};
|
||||
defer lazy_bind.deinit(gpa);
|
||||
try self.collectLazyBindData(&lazy_bind);
|
||||
|
||||
var trie = Trie{};
|
||||
defer trie.deinit(gpa);
|
||||
@ -2180,17 +2186,17 @@ pub const Zld = struct {
|
||||
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,
|
||||
@ -2214,13 +2220,13 @@ pub const Zld = struct {
|
||||
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);
|
||||
@ -2231,10 +2237,7 @@ pub const Zld = struct {
|
||||
});
|
||||
|
||||
try self.file.pwriteAll(buffer, rebase_off);
|
||||
|
||||
const offset = math.cast(usize, lazy_bind_off - rebase_off) orelse return error.Overflow;
|
||||
const size = math.cast(usize, lazy_bind_size) orelse return error.Overflow;
|
||||
try self.populateLazyBindOffsetsInStubHelper(buffer[offset..][0..size]);
|
||||
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);
|
||||
@ -2246,116 +2249,37 @@ pub const Zld = struct {
|
||||
self.dyld_info_cmd.export_size = @intCast(u32, export_size_aligned);
|
||||
}
|
||||
|
||||
fn populateLazyBindOffsetsInStubHelper(self: *Zld, buffer: []const u8) !void {
|
||||
const gpa = self.gpa;
|
||||
fn populateLazyBindOffsetsInStubHelper(self: *Zld, lazy_bind: LazyBind) !void {
|
||||
if (lazy_bind.size() == 0) return;
|
||||
|
||||
const stub_helper_section_index = self.getSectionByName("__TEXT", "__stub_helper") orelse return;
|
||||
const la_symbol_ptr_section_index = self.getSectionByName("__DATA", "__la_symbol_ptr") orelse return;
|
||||
|
||||
if (self.stub_helper_preamble_sym_index == null) return;
|
||||
const stub_helper_section_index = self.getSectionByName("__TEXT", "__stub_helper").?;
|
||||
assert(self.stub_helper_preamble_sym_index != null);
|
||||
|
||||
const section = self.sections.get(stub_helper_section_index);
|
||||
const last_atom_index = section.last_atom_index;
|
||||
|
||||
var table = std.AutoHashMap(i64, AtomIndex).init(gpa);
|
||||
defer table.deinit();
|
||||
|
||||
{
|
||||
var stub_atom_index = last_atom_index;
|
||||
var laptr_atom_index = self.sections.items(.last_atom_index)[la_symbol_ptr_section_index];
|
||||
|
||||
const base_addr = blk: {
|
||||
const segment_index = self.getSegmentByName("__DATA").?;
|
||||
const seg = self.segments.items[segment_index];
|
||||
break :blk seg.vmaddr;
|
||||
};
|
||||
|
||||
while (true) {
|
||||
const stub_atom = self.getAtom(stub_atom_index);
|
||||
const laptr_atom = self.getAtom(laptr_atom_index);
|
||||
const laptr_off = blk: {
|
||||
const sym = self.getSymbolPtr(laptr_atom.getSymbolWithLoc());
|
||||
break :blk @intCast(i64, sym.n_value - base_addr);
|
||||
};
|
||||
|
||||
try table.putNoClobber(laptr_off, stub_atom_index);
|
||||
|
||||
if (laptr_atom.prev_index) |prev_index| {
|
||||
laptr_atom_index = prev_index;
|
||||
stub_atom_index = stub_atom.prev_index.?;
|
||||
} 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.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_index = section.first_atom_index;
|
||||
atom_index = self.getAtom(atom_index).next_index.?; // skip preamble
|
||||
|
||||
while (offsets.popOrNull()) |bind_offset| {
|
||||
const atom_index = table.get(bind_offset.sym_offset).?;
|
||||
var index: usize = 0;
|
||||
while (true) {
|
||||
const atom = self.getAtom(atom_index);
|
||||
const sym = self.getSymbol(atom.getSymbolWithLoc());
|
||||
const atom_sym = self.getSymbol(atom.getSymbolWithLoc());
|
||||
const file_offset = header.offset + atom_sym.n_value - header.addr + stub_offset;
|
||||
const bind_offset = lazy_bind.offsets.items[index];
|
||||
|
||||
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 0x{x} in stub helper at 0x{x}", .{ bind_offset, file_offset });
|
||||
|
||||
log.debug("writing lazy bind offset in stub helper of 0x{x} for symbol {s} at offset 0x{x}", .{
|
||||
bind_offset.offset,
|
||||
self.getSymbolName(atom.getSymbolWithLoc()),
|
||||
file_offset,
|
||||
});
|
||||
try self.file.pwriteAll(mem.asBytes(&bind_offset), file_offset);
|
||||
|
||||
try self.file.pwriteAll(&buf, file_offset);
|
||||
if (atom.next_index) |next_index| {
|
||||
atom_index = next_index;
|
||||
index += 1;
|
||||
} else break;
|
||||
}
|
||||
}
|
||||
|
||||
@ -3018,12 +2942,17 @@ pub const Zld = struct {
|
||||
}
|
||||
|
||||
/// Returns symbol described by `sym_with_loc` descriptor.
|
||||
pub fn getSymbol(self: *Zld, sym_with_loc: SymbolWithLoc) macho.nlist_64 {
|
||||
return self.getSymbolPtr(sym_with_loc).*;
|
||||
pub fn getSymbol(self: *const Zld, sym_with_loc: SymbolWithLoc) macho.nlist_64 {
|
||||
if (sym_with_loc.getFile()) |file| {
|
||||
const object = &self.objects.items[file];
|
||||
return object.symtab[sym_with_loc.sym_index];
|
||||
} else {
|
||||
return self.locals.items[sym_with_loc.sym_index];
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns name of the symbol described by `sym_with_loc` descriptor.
|
||||
pub fn getSymbolName(self: *Zld, sym_with_loc: SymbolWithLoc) []const u8 {
|
||||
pub fn getSymbolName(self: *const Zld, sym_with_loc: SymbolWithLoc) []const u8 {
|
||||
if (sym_with_loc.getFile()) |file| {
|
||||
const object = self.objects.items[file];
|
||||
return object.getSymbolName(sym_with_loc.sym_index);
|
||||
|
||||
@ -12,18 +12,18 @@ pub fn build(b: *Builder) void {
|
||||
.os_tag = .macos,
|
||||
};
|
||||
|
||||
testUuid(b, test_step, .ReleaseSafe, aarch64_macos, "46b333df88f5314686fc0cba3b939ca8");
|
||||
testUuid(b, test_step, .ReleaseFast, aarch64_macos, "46b333df88f5314686fc0cba3b939ca8");
|
||||
testUuid(b, test_step, .ReleaseSmall, aarch64_macos, "46b333df88f5314686fc0cba3b939ca8");
|
||||
testUuid(b, test_step, .ReleaseSafe, aarch64_macos, "af0f4c21a07c30daba59213d80262e45");
|
||||
testUuid(b, test_step, .ReleaseFast, aarch64_macos, "af0f4c21a07c30daba59213d80262e45");
|
||||
testUuid(b, test_step, .ReleaseSmall, aarch64_macos, "af0f4c21a07c30daba59213d80262e45");
|
||||
|
||||
const x86_64_macos = std.zig.CrossTarget{
|
||||
.cpu_arch = .x86_64,
|
||||
.os_tag = .macos,
|
||||
};
|
||||
|
||||
testUuid(b, test_step, .ReleaseSafe, x86_64_macos, "342ac765194131e1bad5692b9e0e54a4");
|
||||
testUuid(b, test_step, .ReleaseFast, x86_64_macos, "342ac765194131e1bad5692b9e0e54a4");
|
||||
testUuid(b, test_step, .ReleaseSmall, x86_64_macos, "f119310e24773ecf8ec42e09d0379dad");
|
||||
testUuid(b, test_step, .ReleaseSafe, x86_64_macos, "63f47191c7153f5fba48bd63cb2f5f57");
|
||||
testUuid(b, test_step, .ReleaseFast, x86_64_macos, "63f47191c7153f5fba48bd63cb2f5f57");
|
||||
testUuid(b, test_step, .ReleaseSmall, x86_64_macos, "e7bba66220e33eda9e73ab293ccf93d2");
|
||||
}
|
||||
|
||||
fn testUuid(
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user