mirror of
https://github.com/ziglang/zig.git
synced 2026-01-12 10:25:13 +00:00
macho: refactor writing and managing externs
This commit is contained in:
parent
21c7217e09
commit
b86d0e488b
@ -1861,29 +1861,27 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
|
||||
}
|
||||
} else if (func_value.castTag(.extern_fn)) |func_payload| {
|
||||
const decl = func_payload.data;
|
||||
// We don't free the decl_name immediately unless it already exists.
|
||||
// If it doesn't, it will get autofreed when we clean up the extern symbol table.
|
||||
const decl_name = try std.fmt.allocPrint(self.bin_file.allocator, "_{s}", .{decl.name});
|
||||
const exists: bool = macho_file.externs.contains(decl_name);
|
||||
const already_defined = macho_file.extern_lazy_symbols.contains(decl_name);
|
||||
const symbol: u32 = blk: {
|
||||
if (macho_file.externs.get(decl_name)) |index| {
|
||||
if (macho_file.extern_lazy_symbols.get(decl_name)) |sym| {
|
||||
self.bin_file.allocator.free(decl_name);
|
||||
break :blk index;
|
||||
break :blk sym.index;
|
||||
} else {
|
||||
const extern_index = @intCast(u32, macho_file.undef_symbols.items.len - 1); // TODO
|
||||
try macho_file.externs.putNoClobber(self.bin_file.allocator, decl_name, extern_index);
|
||||
const name = try macho_file.makeString(decl_name);
|
||||
try macho_file.undef_symbols.append(self.bin_file.allocator, .{
|
||||
.n_strx = name,
|
||||
.n_type = std.macho.N_UNDF | std.macho.N_EXT,
|
||||
.n_sect = 0,
|
||||
.n_desc = std.macho.REFERENCE_FLAG_UNDEFINED_NON_LAZY | std.macho.N_SYMBOL_RESOLVER,
|
||||
.n_value = 0,
|
||||
const index = @intCast(u32, macho_file.extern_lazy_symbols.items().len);
|
||||
try macho_file.extern_lazy_symbols.putNoClobber(self.bin_file.allocator, decl_name, .{
|
||||
.name = decl_name,
|
||||
.dylib_ordinal = 1, // TODO this is now hardcoded, since we only support libSystem.
|
||||
.index = index,
|
||||
});
|
||||
break :blk extern_index;
|
||||
break :blk index;
|
||||
}
|
||||
};
|
||||
try macho_file.stub_fixups.append(self.bin_file.allocator, .{
|
||||
.symbol = symbol,
|
||||
.exists = exists,
|
||||
.already_defined = already_defined,
|
||||
.start = self.code.items.len,
|
||||
.len = 4,
|
||||
});
|
||||
|
||||
@ -105,16 +105,17 @@ entry_addr: ?u64 = null,
|
||||
/// Table of all local symbols
|
||||
/// Internally references string table for names (which are optional).
|
||||
local_symbols: std.ArrayListUnmanaged(macho.nlist_64) = .{},
|
||||
/// Table of all defined global symbols
|
||||
/// Table of all global symbols
|
||||
global_symbols: std.ArrayListUnmanaged(macho.nlist_64) = .{},
|
||||
/// Table of all undefined symbols
|
||||
undef_symbols: std.ArrayListUnmanaged(macho.nlist_64) = .{},
|
||||
/// Table of all extern nonlazy symbols, indexed by name.
|
||||
extern_nonlazy_symbols: std.StringArrayHashMapUnmanaged(ExternSymbol) = .{},
|
||||
/// Table of all extern lazy symbols, indexed by name.
|
||||
extern_lazy_symbols: std.StringArrayHashMapUnmanaged(ExternSymbol) = .{},
|
||||
|
||||
local_symbol_free_list: std.ArrayListUnmanaged(u32) = .{},
|
||||
global_symbol_free_list: std.ArrayListUnmanaged(u32) = .{},
|
||||
offset_table_free_list: std.ArrayListUnmanaged(u32) = .{},
|
||||
|
||||
dyld_stub_binder_index: ?u16 = null,
|
||||
stub_helper_stubs_start_off: ?u64 = null,
|
||||
|
||||
/// Table of symbol names aka the string table.
|
||||
@ -123,13 +124,6 @@ string_table: std.ArrayListUnmanaged(u8) = .{},
|
||||
/// Table of trampolines to the actual symbols in __text section.
|
||||
offset_table: std.ArrayListUnmanaged(u64) = .{},
|
||||
|
||||
/// Table of rebase info entries.
|
||||
rebase_info_table: RebaseInfoTable = .{},
|
||||
/// Table of binding info entries.
|
||||
binding_info_table: BindingInfoTable = .{},
|
||||
/// Table of lazy binding info entries.
|
||||
lazy_binding_info_table: LazyBindingInfoTable = .{},
|
||||
|
||||
error_flags: File.ErrorFlags = File.ErrorFlags{},
|
||||
|
||||
offset_table_count_dirty: bool = false,
|
||||
@ -167,11 +161,10 @@ last_text_block: ?*TextBlock = null,
|
||||
pie_fixups: std.ArrayListUnmanaged(PieFixup) = .{},
|
||||
|
||||
stub_fixups: std.ArrayListUnmanaged(StubFixup) = .{},
|
||||
externs: std.StringHashMapUnmanaged(u32) = .{},
|
||||
|
||||
pub const StubFixup = struct {
|
||||
symbol: u32,
|
||||
exists: bool,
|
||||
already_defined: bool,
|
||||
start: usize,
|
||||
len: usize,
|
||||
};
|
||||
@ -920,42 +913,42 @@ fn linkWithLLD(self: *MachO, comp: *Compilation) !void {
|
||||
return error.NoSymbolTableFound;
|
||||
}
|
||||
|
||||
// Parse dyld info
|
||||
try self.parseBindingInfoTable();
|
||||
try self.parseLazyBindingInfoTable();
|
||||
// // Parse dyld info
|
||||
// try self.parseBindingInfoTable();
|
||||
// try self.parseLazyBindingInfoTable();
|
||||
|
||||
// Update the dylib ordinals.
|
||||
self.binding_info_table.dylib_ordinal = next_ordinal;
|
||||
for (self.lazy_binding_info_table.symbols.items) |*symbol| {
|
||||
symbol.dylib_ordinal = next_ordinal;
|
||||
}
|
||||
// // Update the dylib ordinals.
|
||||
// self.binding_info_table.dylib_ordinal = next_ordinal;
|
||||
// for (self.lazy_binding_info_table.symbols.items) |*symbol| {
|
||||
// symbol.dylib_ordinal = next_ordinal;
|
||||
// }
|
||||
|
||||
// Write updated dyld info.
|
||||
const dyld_info = self.load_commands.items[self.dyld_info_cmd_index.?].DyldInfoOnly;
|
||||
{
|
||||
const size = try self.binding_info_table.calcSize();
|
||||
assert(dyld_info.bind_size >= size);
|
||||
// // Write updated dyld info.
|
||||
// const dyld_info = self.load_commands.items[self.dyld_info_cmd_index.?].DyldInfoOnly;
|
||||
// {
|
||||
// const size = try self.binding_info_table.calcSize();
|
||||
// assert(dyld_info.bind_size >= size);
|
||||
|
||||
var buffer = try self.base.allocator.alloc(u8, @intCast(usize, size));
|
||||
defer self.base.allocator.free(buffer);
|
||||
// var buffer = try self.base.allocator.alloc(u8, @intCast(usize, size));
|
||||
// defer self.base.allocator.free(buffer);
|
||||
|
||||
var stream = std.io.fixedBufferStream(buffer);
|
||||
try self.binding_info_table.write(stream.writer());
|
||||
// var stream = std.io.fixedBufferStream(buffer);
|
||||
// try self.binding_info_table.write(stream.writer());
|
||||
|
||||
try self.base.file.?.pwriteAll(buffer, dyld_info.bind_off);
|
||||
}
|
||||
{
|
||||
const size = try self.lazy_binding_info_table.calcSize();
|
||||
assert(dyld_info.lazy_bind_size >= size);
|
||||
// try self.base.file.?.pwriteAll(buffer, dyld_info.bind_off);
|
||||
// }
|
||||
// {
|
||||
// const size = try self.lazy_binding_info_table.calcSize();
|
||||
// assert(dyld_info.lazy_bind_size >= size);
|
||||
|
||||
var buffer = try self.base.allocator.alloc(u8, @intCast(usize, size));
|
||||
defer self.base.allocator.free(buffer);
|
||||
// var buffer = try self.base.allocator.alloc(u8, @intCast(usize, size));
|
||||
// defer self.base.allocator.free(buffer);
|
||||
|
||||
var stream = std.io.fixedBufferStream(buffer);
|
||||
try self.lazy_binding_info_table.write(stream.writer());
|
||||
// var stream = std.io.fixedBufferStream(buffer);
|
||||
// try self.lazy_binding_info_table.write(stream.writer());
|
||||
|
||||
try self.base.file.?.pwriteAll(buffer, dyld_info.lazy_bind_off);
|
||||
}
|
||||
// try self.base.file.?.pwriteAll(buffer, dyld_info.lazy_bind_off);
|
||||
// }
|
||||
|
||||
// Write updated load commands and the header
|
||||
try self.writeLoadCommands();
|
||||
@ -1037,14 +1030,13 @@ pub fn deinit(self: *MachO) void {
|
||||
if (self.d_sym) |*ds| {
|
||||
ds.deinit(self.base.allocator);
|
||||
}
|
||||
self.binding_info_table.deinit(self.base.allocator);
|
||||
self.lazy_binding_info_table.deinit(self.base.allocator);
|
||||
self.pie_fixups.deinit(self.base.allocator);
|
||||
self.text_block_free_list.deinit(self.base.allocator);
|
||||
self.offset_table.deinit(self.base.allocator);
|
||||
self.offset_table_free_list.deinit(self.base.allocator);
|
||||
self.string_table.deinit(self.base.allocator);
|
||||
self.undef_symbols.deinit(self.base.allocator);
|
||||
self.extern_lazy_symbols.deinit(self.base.allocator);
|
||||
self.extern_nonlazy_symbols.deinit(self.base.allocator);
|
||||
self.global_symbols.deinit(self.base.allocator);
|
||||
self.global_symbol_free_list.deinit(self.base.allocator);
|
||||
self.local_symbols.deinit(self.base.allocator);
|
||||
@ -1261,59 +1253,28 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void {
|
||||
}
|
||||
|
||||
// Resolve stubs (if any)
|
||||
const stubs = &text_segment.sections.items[self.stubs_section_index.?];
|
||||
const stub_h = &text_segment.sections.items[self.stub_helper_section_index.?];
|
||||
const data_segment = &self.load_commands.items[self.data_segment_cmd_index.?].Segment;
|
||||
const la_ptr = &data_segment.sections.items[self.la_symbol_ptr_section_index.?];
|
||||
const stubs = text_segment.sections.items[self.stubs_section_index.?];
|
||||
for (self.stub_fixups.items) |fixup| {
|
||||
// TODO increment offset for stub writing
|
||||
const stub_addr = stubs.addr + fixup.symbol * stubs.reserved2;
|
||||
const text_addr = symbol.n_value + fixup.start;
|
||||
const displacement = @intCast(u32, stub_addr - text_addr);
|
||||
var placeholder = code_buffer.items[fixup.start..][0..fixup.len];
|
||||
mem.writeIntSliceLittle(u32, placeholder, aarch64.Instruction.bl(@intCast(i28, displacement)).toU32());
|
||||
switch (self.base.options.target.cpu.arch) {
|
||||
.x86_64 => return error.TODOImplementStubFixupsForx86_64,
|
||||
.aarch64 => {
|
||||
mem.writeIntSliceLittle(u32, placeholder, aarch64.Instruction.bl(@intCast(i28, displacement)).toU32());
|
||||
},
|
||||
else => unreachable,
|
||||
}
|
||||
if (!fixup.already_defined) {
|
||||
try self.writeStub(fixup.symbol);
|
||||
try self.writeStubInStubHelper(fixup.symbol);
|
||||
try self.writeLazySymbolPointer(fixup.symbol);
|
||||
|
||||
if (!fixup.exists) {
|
||||
const stub_off = self.stub_helper_stubs_start_off.? + fixup.symbol * 3 * @sizeOf(u32);
|
||||
const end = stub_h.addr + stub_off - stub_h.offset;
|
||||
var buf: [@sizeOf(u64)]u8 = undefined;
|
||||
mem.writeIntLittle(u64, &buf, end);
|
||||
try self.base.file.?.pwriteAll(&buf, la_ptr.offset + fixup.symbol * @sizeOf(u64));
|
||||
|
||||
const la_ptr_addr = la_ptr.addr + fixup.symbol * @sizeOf(u64);
|
||||
const displacement2 = la_ptr_addr - stub_addr;
|
||||
var ccode: [2 * @sizeOf(u32)]u8 = undefined;
|
||||
mem.writeIntLittle(u32, ccode[0..4], aarch64.Instruction.ldr(.x16, .{
|
||||
.literal = @intCast(u19, displacement2 / 4),
|
||||
}).toU32());
|
||||
mem.writeIntLittle(u32, ccode[4..8], aarch64.Instruction.br(.x16).toU32());
|
||||
try self.base.file.?.pwriteAll(&ccode, stubs.offset + fixup.symbol * stubs.reserved2);
|
||||
|
||||
const displacement3 = @intCast(i64, stub_h.addr) - @intCast(i64, end + 4);
|
||||
var cccode: [3 * @sizeOf(u32)]u8 = undefined;
|
||||
mem.writeIntLittle(u32, cccode[0..4], aarch64.Instruction.ldr(.w16, .{
|
||||
.literal = 0x2,
|
||||
}).toU32());
|
||||
mem.writeIntLittle(u32, cccode[4..8], aarch64.Instruction.b(@intCast(i28, displacement3)).toU32());
|
||||
mem.writeIntLittle(u32, cccode[8..12], fixup.symbol * 0xd);
|
||||
try self.base.file.?.pwriteAll(&cccode, stub_off);
|
||||
|
||||
try self.rebase_info_table.symbols.append(self.base.allocator, .{
|
||||
.segment = 3,
|
||||
.offset = fixup.symbol * stubs.reserved2,
|
||||
});
|
||||
const extern_sym = &self.extern_lazy_symbols.items()[fixup.symbol].value;
|
||||
extern_sym.segment = self.data_segment_cmd_index.?;
|
||||
extern_sym.offset = fixup.symbol * @sizeOf(u64);
|
||||
self.rebase_info_dirty = true;
|
||||
|
||||
const sym = self.undef_symbols.items[fixup.symbol + 1];
|
||||
const name_str = self.getString(sym.n_strx);
|
||||
var name = try self.base.allocator.alloc(u8, name_str.len);
|
||||
mem.copy(u8, name, name_str);
|
||||
try self.lazy_binding_info_table.symbols.append(self.base.allocator, .{
|
||||
.segment = 3,
|
||||
.offset = fixup.symbol * @sizeOf(u64),
|
||||
.dylib_ordinal = 1,
|
||||
.name = name,
|
||||
});
|
||||
self.lazy_binding_info_dirty = true;
|
||||
}
|
||||
}
|
||||
@ -2080,51 +2041,51 @@ pub fn populateMissingMetadata(self: *MachO) !void {
|
||||
self.header_dirty = true;
|
||||
self.load_commands_dirty = true;
|
||||
}
|
||||
if (self.dyld_stub_binder_index == null) {
|
||||
self.dyld_stub_binder_index = @intCast(u16, self.undef_symbols.items.len);
|
||||
const name = try self.makeString("dyld_stub_binder");
|
||||
try self.undef_symbols.append(self.base.allocator, .{
|
||||
.n_strx = name,
|
||||
.n_type = macho.N_UNDF | macho.N_EXT,
|
||||
.n_sect = 0,
|
||||
.n_desc = macho.REFERENCE_FLAG_UNDEFINED_NON_LAZY | macho.N_SYMBOL_RESOLVER,
|
||||
.n_value = 0,
|
||||
});
|
||||
|
||||
self.binding_info_table.dylib_ordinal = 1;
|
||||
const nn = self.getString(name);
|
||||
var n = try self.base.allocator.alloc(u8, nn.len);
|
||||
mem.copy(u8, n, nn);
|
||||
try self.binding_info_table.symbols.append(self.base.allocator, .{
|
||||
.name = n,
|
||||
.segment = 2,
|
||||
.offset = 0,
|
||||
if (!self.extern_nonlazy_symbols.contains("dyld_stub_binder")) {
|
||||
const index = @intCast(u32, self.extern_nonlazy_symbols.items().len);
|
||||
const name = try std.fmt.allocPrint(self.base.allocator, "dyld_stub_binder", .{});
|
||||
try self.extern_nonlazy_symbols.putNoClobber(self.base.allocator, name, .{
|
||||
.name = name,
|
||||
.dylib_ordinal = 1, // TODO this is currently hardcoded.
|
||||
.index = index,
|
||||
.segment = self.data_const_segment_cmd_index.?,
|
||||
.offset = index * @sizeOf(u64),
|
||||
});
|
||||
self.binding_info_dirty = true;
|
||||
}
|
||||
if (self.stub_helper_stubs_start_off == null) {
|
||||
const text = &self.load_commands.items[self.text_segment_cmd_index.?].Segment;
|
||||
const sh = &text.sections.items[self.stub_helper_section_index.?];
|
||||
const data = &self.load_commands.items[self.data_segment_cmd_index.?].Segment;
|
||||
const data_data = &data.sections.items[self.data_section_index.?];
|
||||
const displacement = data_data.addr - sh.addr;
|
||||
var code: [4 * @sizeOf(u32)]u8 = undefined;
|
||||
mem.writeIntLittle(u32, code[0..4], aarch64.Instruction.adr(.x17, @intCast(i21, displacement)).toU32());
|
||||
mem.writeIntLittle(u32, code[4..8], aarch64.Instruction.stp(
|
||||
.x16,
|
||||
.x17,
|
||||
aarch64.Register.sp,
|
||||
aarch64.Instruction.LoadStorePairOffset.pre_index(-16),
|
||||
).toU32());
|
||||
const dc = &self.load_commands.items[self.data_const_segment_cmd_index.?].Segment;
|
||||
const got = &dc.sections.items[self.data_got_section_index.?];
|
||||
const displacement2 = got.addr - sh.addr - 2 * @sizeOf(u32);
|
||||
mem.writeIntLittle(u32, code[8..12], aarch64.Instruction.ldr(.x16, .{
|
||||
.literal = @intCast(u19, displacement2 / 4),
|
||||
}).toU32());
|
||||
mem.writeIntLittle(u32, code[12..16], aarch64.Instruction.br(.x16).toU32());
|
||||
self.stub_helper_stubs_start_off = sh.offset + 4 * @sizeOf(u32);
|
||||
try self.base.file.?.pwriteAll(&code, sh.offset);
|
||||
const text_segment = &self.load_commands.items[self.text_segment_cmd_index.?].Segment;
|
||||
const stub_helper = &text_segment.sections.items[self.stub_helper_section_index.?];
|
||||
const data_segment = &self.load_commands.items[self.data_segment_cmd_index.?].Segment;
|
||||
const data = &data_segment.sections.items[self.data_section_index.?];
|
||||
const data_const_segment = &self.load_commands.items[self.data_const_segment_cmd_index.?].Segment;
|
||||
const got = &data_const_segment.sections.items[self.data_got_section_index.?];
|
||||
switch (self.base.options.target.cpu.arch) {
|
||||
.x86_64 => return error.TODOImplementStubHelperForX86_64,
|
||||
.aarch64 => {
|
||||
var code: [4 * @sizeOf(u32)]u8 = undefined;
|
||||
{
|
||||
const displacement = data.addr - stub_helper.addr;
|
||||
mem.writeIntLittle(u32, code[0..4], aarch64.Instruction.adr(.x17, @intCast(i21, displacement)).toU32());
|
||||
}
|
||||
mem.writeIntLittle(u32, code[4..8], aarch64.Instruction.stp(
|
||||
.x16,
|
||||
.x17,
|
||||
aarch64.Register.sp,
|
||||
aarch64.Instruction.LoadStorePairOffset.pre_index(-16),
|
||||
).toU32());
|
||||
{
|
||||
const displacement = got.addr - stub_helper.addr - 2 * @sizeOf(u32);
|
||||
mem.writeIntLittle(u32, code[8..12], aarch64.Instruction.ldr(.x16, .{
|
||||
.literal = @intCast(u19, displacement / 4),
|
||||
}).toU32());
|
||||
}
|
||||
mem.writeIntLittle(u32, code[12..16], aarch64.Instruction.br(.x16).toU32());
|
||||
self.stub_helper_stubs_start_off = stub_helper.offset + 4 * @sizeOf(u32);
|
||||
try self.base.file.?.pwriteAll(&code, stub_helper.offset);
|
||||
},
|
||||
else => unreachable,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -2460,11 +2421,73 @@ fn writeOffsetTableEntry(self: *MachO, index: usize) !void {
|
||||
try self.base.file.?.pwriteAll(&code, off);
|
||||
}
|
||||
|
||||
fn writeLazySymbolPointer(self: *MachO, index: u32) !void {
|
||||
const text_segment = self.load_commands.items[self.text_segment_cmd_index.?].Segment;
|
||||
const stub_helper = text_segment.sections.items[self.stub_helper_section_index.?];
|
||||
const data_segment = self.load_commands.items[self.data_segment_cmd_index.?].Segment;
|
||||
const la_symbol_ptr = data_segment.sections.items[self.la_symbol_ptr_section_index.?];
|
||||
|
||||
const stub_off = self.stub_helper_stubs_start_off.? + index * 3 * @sizeOf(u32);
|
||||
const end = stub_helper.addr + stub_off - stub_helper.offset;
|
||||
var buf: [@sizeOf(u64)]u8 = undefined;
|
||||
mem.writeIntLittle(u64, &buf, end);
|
||||
const off = la_symbol_ptr.offset + index * @sizeOf(u64);
|
||||
log.debug("writing lazy symbol pointer entry 0x{x} at 0x{x}", .{ end, off });
|
||||
try self.base.file.?.pwriteAll(&buf, off);
|
||||
}
|
||||
|
||||
fn writeStub(self: *MachO, index: u32) !void {
|
||||
const text_segment = self.load_commands.items[self.text_segment_cmd_index.?].Segment;
|
||||
const stubs = text_segment.sections.items[self.stubs_section_index.?];
|
||||
const data_segment = self.load_commands.items[self.data_segment_cmd_index.?].Segment;
|
||||
const la_symbol_ptr = data_segment.sections.items[self.la_symbol_ptr_section_index.?];
|
||||
|
||||
const stub_off = stubs.offset + index * stubs.reserved2;
|
||||
const stub_addr = stubs.addr + index * stubs.reserved2;
|
||||
const la_ptr_addr = la_symbol_ptr.addr + index * @sizeOf(u64);
|
||||
const displacement = la_ptr_addr - stub_addr;
|
||||
log.debug("writing stub at 0x{x}", .{stub_off});
|
||||
switch (self.base.options.target.cpu.arch) {
|
||||
.x86_64 => return error.TODOImplementWritingStubsForx86_64,
|
||||
.aarch64 => {
|
||||
var code: [2 * @sizeOf(u32)]u8 = undefined;
|
||||
mem.writeIntLittle(u32, code[0..4], aarch64.Instruction.ldr(.x16, .{
|
||||
.literal = @intCast(u19, displacement / 4),
|
||||
}).toU32());
|
||||
mem.writeIntLittle(u32, code[4..8], aarch64.Instruction.br(.x16).toU32());
|
||||
try self.base.file.?.pwriteAll(&code, stub_off);
|
||||
},
|
||||
else => unreachable,
|
||||
}
|
||||
}
|
||||
|
||||
fn writeStubInStubHelper(self: *MachO, index: u32) !void {
|
||||
const text_segment = self.load_commands.items[self.text_segment_cmd_index.?].Segment;
|
||||
const stub_helper = text_segment.sections.items[self.stub_helper_section_index.?];
|
||||
|
||||
const stub_off = self.stub_helper_stubs_start_off.? + index * 3 * @sizeOf(u32);
|
||||
const end = stub_helper.addr + stub_off - stub_helper.offset;
|
||||
const displacement = @intCast(i64, stub_helper.addr) - @intCast(i64, end + 4);
|
||||
switch (self.base.options.target.cpu.arch) {
|
||||
.x86_64 => return error.TODOImplementWritingStubsInStubHelperForx86_64,
|
||||
.aarch64 => {
|
||||
var code: [3 * @sizeOf(u32)]u8 = undefined;
|
||||
mem.writeIntLittle(u32, code[0..4], aarch64.Instruction.ldr(.w16, .{
|
||||
.literal = 0x2,
|
||||
}).toU32());
|
||||
mem.writeIntLittle(u32, code[4..8], aarch64.Instruction.b(@intCast(i28, displacement)).toU32());
|
||||
mem.writeIntLittle(u32, code[8..12], index * 0xd); // TODO This is the size of lazy binding opcode block.
|
||||
try self.base.file.?.pwriteAll(&code, stub_off);
|
||||
},
|
||||
else => unreachable,
|
||||
}
|
||||
}
|
||||
|
||||
fn relocateSymbolTable(self: *MachO) !void {
|
||||
const symtab = &self.load_commands.items[self.symtab_cmd_index.?].Symtab;
|
||||
const nlocals = self.local_symbols.items.len;
|
||||
const nglobals = self.global_symbols.items.len;
|
||||
const nundefs = self.undef_symbols.items.len;
|
||||
const nundefs = self.extern_lazy_symbols.items().len + self.extern_nonlazy_symbols.items().len;
|
||||
const nsyms = nlocals + nglobals + nundefs;
|
||||
|
||||
if (symtab.nsyms < nsyms) {
|
||||
@ -2509,7 +2532,31 @@ fn writeAllGlobalAndUndefSymbols(self: *MachO) !void {
|
||||
const symtab = &self.load_commands.items[self.symtab_cmd_index.?].Symtab;
|
||||
const nlocals = self.local_symbols.items.len;
|
||||
const nglobals = self.global_symbols.items.len;
|
||||
const nundefs = self.undef_symbols.items.len;
|
||||
|
||||
const nundefs = self.extern_lazy_symbols.items().len + self.extern_nonlazy_symbols.items().len;
|
||||
var undefs = std.ArrayList(macho.nlist_64).init(self.base.allocator);
|
||||
defer undefs.deinit();
|
||||
try undefs.ensureCapacity(nundefs);
|
||||
for (self.extern_lazy_symbols.items()) |entry| {
|
||||
const name = try self.makeString(entry.key);
|
||||
undefs.appendAssumeCapacity(.{
|
||||
.n_strx = name,
|
||||
.n_type = std.macho.N_UNDF | std.macho.N_EXT,
|
||||
.n_sect = 0,
|
||||
.n_desc = std.macho.REFERENCE_FLAG_UNDEFINED_NON_LAZY | std.macho.N_SYMBOL_RESOLVER,
|
||||
.n_value = 0,
|
||||
});
|
||||
}
|
||||
for (self.extern_nonlazy_symbols.items()) |entry| {
|
||||
const name = try self.makeString(entry.key);
|
||||
undefs.appendAssumeCapacity(.{
|
||||
.n_strx = name,
|
||||
.n_type = std.macho.N_UNDF | std.macho.N_EXT,
|
||||
.n_sect = 0,
|
||||
.n_desc = std.macho.REFERENCE_FLAG_UNDEFINED_NON_LAZY | std.macho.N_SYMBOL_RESOLVER,
|
||||
.n_value = 0,
|
||||
});
|
||||
}
|
||||
|
||||
const locals_off = symtab.symoff;
|
||||
const locals_size = nlocals * @sizeOf(macho.nlist_64);
|
||||
@ -2521,8 +2568,8 @@ fn writeAllGlobalAndUndefSymbols(self: *MachO) !void {
|
||||
|
||||
const undefs_off = globals_off + globals_size;
|
||||
const undefs_size = nundefs * @sizeOf(macho.nlist_64);
|
||||
log.debug("writing undef symbols from 0x{x} to 0x{x}", .{ undefs_off, undefs_size + undefs_off });
|
||||
try self.base.file.?.pwriteAll(mem.sliceAsBytes(self.undef_symbols.items), undefs_off);
|
||||
log.debug("writing extern symbols from 0x{x} to 0x{x}", .{ undefs_off, undefs_size + undefs_off });
|
||||
try self.base.file.?.pwriteAll(mem.sliceAsBytes(undefs.items), undefs_off);
|
||||
|
||||
// Update dynamic symbol table.
|
||||
const dysymtab = &self.load_commands.items[self.dysymtab_cmd_index.?].Dysymtab;
|
||||
@ -2546,42 +2593,33 @@ fn writeIndirectSymbolTable(self: *MachO) !void {
|
||||
|
||||
var buf: [@sizeOf(u32)]u8 = undefined;
|
||||
var off = dysymtab.indirectsymoff;
|
||||
var idx: u32 = 0;
|
||||
|
||||
stubs.reserved1 = 0;
|
||||
for (self.undef_symbols.items) |sym, i| {
|
||||
if (i == self.dyld_stub_binder_index.?) {
|
||||
continue;
|
||||
}
|
||||
const symtab_idx = @intCast(u32, dysymtab.iundefsym + i);
|
||||
for (self.extern_lazy_symbols.items()) |entry| {
|
||||
const symtab_idx = @intCast(u32, dysymtab.iundefsym + entry.value.index);
|
||||
mem.writeIntLittle(u32, &buf, symtab_idx);
|
||||
try self.base.file.?.pwriteAll(&buf, off);
|
||||
off += @sizeOf(u32);
|
||||
dysymtab.nindirectsyms += 1;
|
||||
idx += 1;
|
||||
}
|
||||
|
||||
got.reserved1 = @intCast(u32, self.undef_symbols.items.len - 1);
|
||||
if (self.dyld_stub_binder_index) |i| {
|
||||
const symtab_idx = i + dysymtab.iundefsym;
|
||||
const base_id = @intCast(u32, self.extern_lazy_symbols.items().len);
|
||||
got.reserved1 = base_id;
|
||||
for (self.extern_nonlazy_symbols.items()) |entry| {
|
||||
const symtab_idx = @intCast(u32, dysymtab.iundefsym + entry.value.index + base_id);
|
||||
mem.writeIntLittle(u32, &buf, symtab_idx);
|
||||
try self.base.file.?.pwriteAll(&buf, off);
|
||||
off += @sizeOf(u32);
|
||||
dysymtab.nindirectsyms += 1;
|
||||
idx += 1;
|
||||
}
|
||||
|
||||
la.reserved1 = got.reserved1 + 1;
|
||||
for (self.undef_symbols.items) |sym, i| {
|
||||
if (i == self.dyld_stub_binder_index.?) {
|
||||
continue;
|
||||
}
|
||||
const symtab_idx = @intCast(u32, dysymtab.iundefsym + i);
|
||||
la.reserved1 = got.reserved1 + @intCast(u32, self.extern_nonlazy_symbols.items().len);
|
||||
for (self.extern_lazy_symbols.items()) |entry| {
|
||||
const symtab_idx = @intCast(u32, dysymtab.iundefsym + entry.value.index);
|
||||
mem.writeIntLittle(u32, &buf, symtab_idx);
|
||||
try self.base.file.?.pwriteAll(&buf, off);
|
||||
off += @sizeOf(u32);
|
||||
dysymtab.nindirectsyms += 1;
|
||||
idx += 1;
|
||||
}
|
||||
}
|
||||
|
||||
@ -2689,12 +2727,19 @@ fn writeRebaseInfoTable(self: *MachO) !void {
|
||||
const tracy = trace(@src());
|
||||
defer tracy.end();
|
||||
|
||||
const size = try self.rebase_info_table.calcSize();
|
||||
var symbols = try self.base.allocator.alloc(*const ExternSymbol, self.extern_lazy_symbols.items().len);
|
||||
defer self.base.allocator.free(symbols);
|
||||
|
||||
for (self.extern_lazy_symbols.items()) |*entry, i| {
|
||||
symbols[i] = &entry.value;
|
||||
}
|
||||
|
||||
const size = try rebaseInfoSize(symbols);
|
||||
var buffer = try self.base.allocator.alloc(u8, @intCast(usize, size));
|
||||
defer self.base.allocator.free(buffer);
|
||||
|
||||
var stream = std.io.fixedBufferStream(buffer);
|
||||
try self.rebase_info_table.write(stream.writer());
|
||||
try writeRebaseInfo(symbols, stream.writer());
|
||||
|
||||
const linkedit_segment = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment;
|
||||
const dyld_info = &self.load_commands.items[self.dyld_info_cmd_index.?].DyldInfoOnly;
|
||||
@ -2720,12 +2765,19 @@ fn writeBindingInfoTable(self: *MachO) !void {
|
||||
const tracy = trace(@src());
|
||||
defer tracy.end();
|
||||
|
||||
const size = try self.binding_info_table.calcSize();
|
||||
var symbols = try self.base.allocator.alloc(*const ExternSymbol, self.extern_nonlazy_symbols.items().len);
|
||||
defer self.base.allocator.free(symbols);
|
||||
|
||||
for (self.extern_nonlazy_symbols.items()) |*entry, i| {
|
||||
symbols[i] = &entry.value;
|
||||
}
|
||||
|
||||
const size = try bindInfoSize(symbols);
|
||||
var buffer = try self.base.allocator.alloc(u8, @intCast(usize, size));
|
||||
defer self.base.allocator.free(buffer);
|
||||
|
||||
var stream = std.io.fixedBufferStream(buffer);
|
||||
try self.binding_info_table.write(stream.writer());
|
||||
try writeBindInfo(symbols, stream.writer());
|
||||
|
||||
const linkedit_segment = self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment;
|
||||
const dyld_info = &self.load_commands.items[self.dyld_info_cmd_index.?].DyldInfoOnly;
|
||||
@ -2748,12 +2800,19 @@ fn writeBindingInfoTable(self: *MachO) !void {
|
||||
fn writeLazyBindingInfoTable(self: *MachO) !void {
|
||||
if (!self.lazy_binding_info_dirty) return;
|
||||
|
||||
const size = try self.lazy_binding_info_table.calcSize();
|
||||
var symbols = try self.base.allocator.alloc(*const ExternSymbol, self.extern_lazy_symbols.items().len);
|
||||
defer self.base.allocator.free(symbols);
|
||||
|
||||
for (self.extern_lazy_symbols.items()) |*entry, i| {
|
||||
symbols[i] = &entry.value;
|
||||
}
|
||||
|
||||
const size = try lazyBindInfoSize(symbols);
|
||||
var buffer = try self.base.allocator.alloc(u8, @intCast(usize, size));
|
||||
defer self.base.allocator.free(buffer);
|
||||
|
||||
var stream = std.io.fixedBufferStream(buffer);
|
||||
try self.lazy_binding_info_table.write(stream.writer());
|
||||
try writeLazyBindInfo(symbols, stream.writer());
|
||||
|
||||
const linkedit_segment = self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment;
|
||||
const dyld_info = &self.load_commands.items[self.dyld_info_cmd_index.?].DyldInfoOnly;
|
||||
@ -3001,7 +3060,7 @@ fn parseBindingInfoTable(self: *MachO) !void {
|
||||
assert(nread == buffer.len);
|
||||
|
||||
var stream = std.io.fixedBufferStream(buffer);
|
||||
try self.binding_info_table.read(stream.reader(), self.base.allocator);
|
||||
// try self.binding_info_table.read(stream.reader(), self.base.allocator);
|
||||
}
|
||||
|
||||
fn parseLazyBindingInfoTable(self: *MachO) !void {
|
||||
@ -3012,5 +3071,5 @@ fn parseLazyBindingInfoTable(self: *MachO) !void {
|
||||
assert(nread == buffer.len);
|
||||
|
||||
var stream = std.io.fixedBufferStream(buffer);
|
||||
try self.lazy_binding_info_table.read(stream.reader(), self.base.allocator);
|
||||
// try self.lazy_binding_info_table.read(stream.reader(), self.base.allocator);
|
||||
}
|
||||
|
||||
@ -6,365 +6,177 @@ const mem = std.mem;
|
||||
const assert = std.debug.assert;
|
||||
const Allocator = mem.Allocator;
|
||||
|
||||
pub const RebaseInfoTable = struct {
|
||||
rebase_type: u8 = macho.REBASE_TYPE_POINTER,
|
||||
symbols: std.ArrayListUnmanaged(Symbol) = .{},
|
||||
pub const ExternSymbol = struct {
|
||||
/// Symbol name.
|
||||
/// We own the memory, therefore we'll need to free it by calling `deinit`.
|
||||
/// In self-hosted, we don't expect it to be null ever.
|
||||
/// However, this is for backwards compatibility with LLD when
|
||||
/// we'll be patching things up post mortem.
|
||||
name: ?[]u8 = null,
|
||||
|
||||
pub const Symbol = struct {
|
||||
segment: u8,
|
||||
offset: i64,
|
||||
};
|
||||
/// Id of the dynamic library where the specified entries can be found.
|
||||
/// Id of 0 means self.
|
||||
/// TODO this should really be an id into the table of all defined
|
||||
/// dylibs.
|
||||
dylib_ordinal: i64 = 0,
|
||||
|
||||
pub fn deinit(self: *RebaseInfoTable, allocator: *Allocator) void {
|
||||
self.symbols.deinit(allocator);
|
||||
}
|
||||
segment: u16 = 0,
|
||||
offset: u32 = 0,
|
||||
addend: ?i32 = null,
|
||||
index: u32,
|
||||
|
||||
/// Write the rebase info table to byte stream.
|
||||
pub fn write(self: RebaseInfoTable, writer: anytype) !void {
|
||||
for (self.symbols.items) |symbol| {
|
||||
try writer.writeByte(macho.REBASE_OPCODE_SET_TYPE_IMM | @truncate(u4, self.rebase_type));
|
||||
try writer.writeByte(macho.REBASE_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB | @truncate(u4, symbol.segment));
|
||||
try leb.writeILEB128(writer, symbol.offset);
|
||||
try writer.writeByte(macho.REBASE_OPCODE_DO_REBASE_IMM_TIMES | @truncate(u4, 1));
|
||||
pub fn deinit(self: *ExternSymbol, allocator: *Allocator) void {
|
||||
if (self.name) |*name| {
|
||||
allocator.free(name);
|
||||
}
|
||||
|
||||
try writer.writeByte(macho.REBASE_OPCODE_DONE);
|
||||
}
|
||||
|
||||
/// Calculate size in bytes of this rebase info table.
|
||||
pub fn calcSize(self: *RebaseInfoTable) !u64 {
|
||||
var stream = std.io.countingWriter(std.io.null_writer);
|
||||
var writer = stream.writer();
|
||||
var size: u64 = 0;
|
||||
|
||||
for (self.symbols.items) |symbol| {
|
||||
size += 2;
|
||||
try leb.writeILEB128(writer, symbol.offset);
|
||||
size += 1;
|
||||
}
|
||||
|
||||
size += 1 + stream.bytes_written;
|
||||
return size;
|
||||
}
|
||||
};
|
||||
|
||||
/// Table of binding info entries used to tell the dyld which
|
||||
/// symbols to bind at loading time.
|
||||
pub const BindingInfoTable = struct {
|
||||
/// Id of the dynamic library where the specified entries can be found.
|
||||
dylib_ordinal: i64 = 0,
|
||||
pub fn rebaseInfoSize(symbols: []*const ExternSymbol) !u64 {
|
||||
var stream = std.io.countingWriter(std.io.null_writer);
|
||||
var writer = stream.writer();
|
||||
var size: u64 = 0;
|
||||
|
||||
/// Binding type; defaults to pointer type.
|
||||
binding_type: u8 = macho.BIND_TYPE_POINTER,
|
||||
|
||||
symbols: std.ArrayListUnmanaged(Symbol) = .{},
|
||||
|
||||
pub const Symbol = struct {
|
||||
/// Symbol name.
|
||||
name: ?[]u8 = null,
|
||||
|
||||
/// Id of the segment where to bind this symbol to.
|
||||
segment: u8,
|
||||
|
||||
/// Offset of this symbol wrt to the segment id encoded in `segment`.
|
||||
offset: i64,
|
||||
|
||||
/// Addend value (if any).
|
||||
addend: ?i64 = null,
|
||||
};
|
||||
|
||||
pub fn deinit(self: *BindingInfoTable, allocator: *Allocator) void {
|
||||
for (self.symbols.items) |*symbol| {
|
||||
if (symbol.name) |name| {
|
||||
allocator.free(name);
|
||||
}
|
||||
}
|
||||
self.symbols.deinit(allocator);
|
||||
for (symbols) |symbol| {
|
||||
size += 2;
|
||||
try leb.writeILEB128(writer, symbol.offset);
|
||||
size += 1;
|
||||
}
|
||||
|
||||
/// Parse the binding info table from byte stream.
|
||||
pub fn read(self: *BindingInfoTable, reader: anytype, allocator: *Allocator) !void {
|
||||
var symbol: Symbol = .{
|
||||
.segment = 0,
|
||||
.offset = 0,
|
||||
};
|
||||
size += 1 + stream.bytes_written;
|
||||
return size;
|
||||
}
|
||||
|
||||
var dylib_ordinal_set = false;
|
||||
var done = false;
|
||||
while (true) {
|
||||
const inst = reader.readByte() catch |err| switch (err) {
|
||||
error.EndOfStream => break,
|
||||
else => return err,
|
||||
};
|
||||
const imm: u8 = inst & macho.BIND_IMMEDIATE_MASK;
|
||||
const opcode: u8 = inst & macho.BIND_OPCODE_MASK;
|
||||
|
||||
switch (opcode) {
|
||||
macho.BIND_OPCODE_DO_BIND => {
|
||||
try self.symbols.append(allocator, symbol);
|
||||
symbol = .{
|
||||
.segment = 0,
|
||||
.offset = 0,
|
||||
};
|
||||
},
|
||||
macho.BIND_OPCODE_DONE => {
|
||||
done = true;
|
||||
break;
|
||||
},
|
||||
macho.BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM => {
|
||||
var name = std.ArrayList(u8).init(allocator);
|
||||
var next = try reader.readByte();
|
||||
while (next != @as(u8, 0)) {
|
||||
try name.append(next);
|
||||
next = try reader.readByte();
|
||||
}
|
||||
symbol.name = name.toOwnedSlice();
|
||||
},
|
||||
macho.BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB => {
|
||||
symbol.segment = imm;
|
||||
symbol.offset = try leb.readILEB128(i64, reader);
|
||||
},
|
||||
macho.BIND_OPCODE_SET_DYLIB_SPECIAL_IMM, macho.BIND_OPCODE_SET_DYLIB_ORDINAL_IMM => {
|
||||
assert(!dylib_ordinal_set);
|
||||
self.dylib_ordinal = imm;
|
||||
},
|
||||
macho.BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB => {
|
||||
assert(!dylib_ordinal_set);
|
||||
self.dylib_ordinal = try leb.readILEB128(i64, reader);
|
||||
},
|
||||
macho.BIND_OPCODE_SET_TYPE_IMM => {
|
||||
self.binding_type = imm;
|
||||
},
|
||||
macho.BIND_OPCODE_SET_ADDEND_SLEB => {
|
||||
symbol.addend = try leb.readILEB128(i64, reader);
|
||||
},
|
||||
else => {
|
||||
std.log.warn("unhandled BIND_OPCODE_: 0x{x}", .{opcode});
|
||||
},
|
||||
}
|
||||
}
|
||||
assert(done);
|
||||
pub fn writeRebaseInfo(symbols: []*const ExternSymbol, writer: anytype) !void {
|
||||
for (symbols) |symbol| {
|
||||
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, symbol.segment));
|
||||
try leb.writeILEB128(writer, symbol.offset);
|
||||
try writer.writeByte(macho.REBASE_OPCODE_DO_REBASE_IMM_TIMES | @truncate(u4, 1));
|
||||
}
|
||||
try writer.writeByte(macho.REBASE_OPCODE_DONE);
|
||||
}
|
||||
|
||||
/// Write the binding info table to byte stream.
|
||||
pub fn write(self: BindingInfoTable, writer: anytype) !void {
|
||||
if (self.dylib_ordinal > 15) {
|
||||
try writer.writeByte(macho.BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB);
|
||||
try leb.writeULEB128(writer, @bitCast(u64, self.dylib_ordinal));
|
||||
} else if (self.dylib_ordinal > 0) {
|
||||
try writer.writeByte(macho.BIND_OPCODE_SET_DYLIB_ORDINAL_IMM | @truncate(u4, @bitCast(u64, self.dylib_ordinal)));
|
||||
} else {
|
||||
try writer.writeByte(macho.BIND_OPCODE_SET_DYLIB_SPECIAL_IMM | @truncate(u4, @bitCast(u64, self.dylib_ordinal)));
|
||||
pub fn bindInfoSize(symbols: []*const ExternSymbol) !u64 {
|
||||
var stream = std.io.countingWriter(std.io.null_writer);
|
||||
var writer = stream.writer();
|
||||
var size: u64 = 0;
|
||||
|
||||
for (symbols) |symbol| {
|
||||
size += 1;
|
||||
if (symbol.dylib_ordinal > 15) {
|
||||
try leb.writeULEB128(writer, @bitCast(u64, symbol.dylib_ordinal));
|
||||
}
|
||||
try writer.writeByte(macho.BIND_OPCODE_SET_TYPE_IMM | @truncate(u4, self.binding_type));
|
||||
size += 1;
|
||||
|
||||
for (self.symbols.items) |symbol| {
|
||||
if (symbol.name) |name| {
|
||||
try writer.writeByte(macho.BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM); // TODO Sometimes we might want to add flags.
|
||||
try writer.writeAll(name);
|
||||
try writer.writeByte(0);
|
||||
}
|
||||
|
||||
try writer.writeByte(macho.BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB | @truncate(u4, symbol.segment));
|
||||
try leb.writeILEB128(writer, symbol.offset);
|
||||
|
||||
if (symbol.addend) |addend| {
|
||||
try writer.writeByte(macho.BIND_OPCODE_SET_ADDEND_SLEB);
|
||||
try leb.writeILEB128(writer, addend);
|
||||
}
|
||||
|
||||
try writer.writeByte(macho.BIND_OPCODE_DO_BIND);
|
||||
}
|
||||
|
||||
try writer.writeByte(macho.BIND_OPCODE_DONE);
|
||||
}
|
||||
|
||||
/// Calculate size in bytes of this binding info table.
|
||||
pub fn calcSize(self: *BindingInfoTable) !u64 {
|
||||
var stream = std.io.countingWriter(std.io.null_writer);
|
||||
var writer = stream.writer();
|
||||
var size: u64 = 1;
|
||||
|
||||
if (self.dylib_ordinal > 15) {
|
||||
try leb.writeULEB128(writer, @bitCast(u64, self.dylib_ordinal));
|
||||
if (symbol.name) |name| {
|
||||
size += 1;
|
||||
size += name.len;
|
||||
size += 1;
|
||||
}
|
||||
|
||||
size += 1;
|
||||
try leb.writeILEB128(writer, symbol.offset);
|
||||
|
||||
for (self.symbols.items) |symbol| {
|
||||
if (symbol.name) |name| {
|
||||
size += 1;
|
||||
size += name.len;
|
||||
size += 1;
|
||||
}
|
||||
|
||||
if (symbol.addend) |addend| {
|
||||
size += 1;
|
||||
try leb.writeILEB128(writer, symbol.offset);
|
||||
try leb.writeILEB128(writer, addend);
|
||||
}
|
||||
|
||||
if (symbol.addend) |addend| {
|
||||
size += 1;
|
||||
try leb.writeILEB128(writer, addend);
|
||||
}
|
||||
size += 2;
|
||||
}
|
||||
|
||||
size += stream.bytes_written;
|
||||
return size;
|
||||
}
|
||||
|
||||
pub fn writeBindInfo(symbols: []*const ExternSymbol, writer: anytype) !void {
|
||||
for (symbols) |symbol| {
|
||||
if (symbol.dylib_ordinal > 15) {
|
||||
try writer.writeByte(macho.BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB);
|
||||
try leb.writeULEB128(writer, @bitCast(u64, symbol.dylib_ordinal));
|
||||
} else if (symbol.dylib_ordinal > 0) {
|
||||
try writer.writeByte(macho.BIND_OPCODE_SET_DYLIB_ORDINAL_IMM | @truncate(u4, @bitCast(u64, symbol.dylib_ordinal)));
|
||||
} else {
|
||||
try writer.writeByte(macho.BIND_OPCODE_SET_DYLIB_SPECIAL_IMM | @truncate(u4, @bitCast(u64, symbol.dylib_ordinal)));
|
||||
}
|
||||
try writer.writeByte(macho.BIND_OPCODE_SET_TYPE_IMM | @truncate(u4, macho.BIND_TYPE_POINTER));
|
||||
|
||||
if (symbol.name) |name| {
|
||||
try writer.writeByte(macho.BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM); // TODO Sometimes we might want to add flags.
|
||||
try writer.writeAll(name);
|
||||
try writer.writeByte(0);
|
||||
}
|
||||
|
||||
try writer.writeByte(macho.BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB | @truncate(u4, symbol.segment));
|
||||
try leb.writeILEB128(writer, symbol.offset);
|
||||
|
||||
if (symbol.addend) |addend| {
|
||||
try writer.writeByte(macho.BIND_OPCODE_SET_ADDEND_SLEB);
|
||||
try leb.writeILEB128(writer, addend);
|
||||
}
|
||||
|
||||
try writer.writeByte(macho.BIND_OPCODE_DO_BIND);
|
||||
try writer.writeByte(macho.BIND_OPCODE_DONE);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn lazyBindInfoSize(symbols: []*const ExternSymbol) !u64 {
|
||||
var stream = std.io.countingWriter(std.io.null_writer);
|
||||
var writer = stream.writer();
|
||||
var size: u64 = 0;
|
||||
|
||||
for (symbols) |symbol| {
|
||||
size += 1;
|
||||
try leb.writeILEB128(writer, symbol.offset);
|
||||
|
||||
if (symbol.addend) |addend| {
|
||||
size += 1;
|
||||
try leb.writeILEB128(writer, addend);
|
||||
}
|
||||
|
||||
size += 1;
|
||||
if (symbol.dylib_ordinal > 15) {
|
||||
try leb.writeULEB128(writer, @bitCast(u64, symbol.dylib_ordinal));
|
||||
}
|
||||
if (symbol.name) |name| {
|
||||
size += 1;
|
||||
size += name.len;
|
||||
size += 1;
|
||||
}
|
||||
|
||||
size += 1 + stream.bytes_written;
|
||||
return size;
|
||||
}
|
||||
};
|
||||
|
||||
/// Table of lazy binding info entries used to tell the dyld which
|
||||
/// symbols to lazily bind at first load of a dylib.
|
||||
pub const LazyBindingInfoTable = struct {
|
||||
symbols: std.ArrayListUnmanaged(Symbol) = .{},
|
||||
|
||||
pub const Symbol = struct {
|
||||
/// Symbol name.
|
||||
name: ?[]u8 = null,
|
||||
|
||||
/// Offset of this symbol wrt to the segment id encoded in `segment`.
|
||||
offset: i64,
|
||||
|
||||
/// Id of the dylib where this symbol is expected to reside.
|
||||
/// Positive ordinals point at dylibs imported with LC_LOAD_DYLIB,
|
||||
/// 0 means this binary, -1 the main executable, and -2 flat lookup.
|
||||
dylib_ordinal: i64,
|
||||
|
||||
/// Id of the segment where to bind this symbol to.
|
||||
segment: u8,
|
||||
|
||||
/// Addend value (if any).
|
||||
addend: ?i64 = null,
|
||||
};
|
||||
|
||||
pub fn deinit(self: *LazyBindingInfoTable, allocator: *Allocator) void {
|
||||
for (self.symbols.items) |*symbol| {
|
||||
if (symbol.name) |name| {
|
||||
allocator.free(name);
|
||||
}
|
||||
}
|
||||
self.symbols.deinit(allocator);
|
||||
size += 2;
|
||||
}
|
||||
|
||||
/// Parse the binding info table from byte stream.
|
||||
pub fn read(self: *LazyBindingInfoTable, reader: anytype, allocator: *Allocator) !void {
|
||||
var symbol: Symbol = .{
|
||||
.offset = 0,
|
||||
.segment = 0,
|
||||
.dylib_ordinal = 0,
|
||||
};
|
||||
size += stream.bytes_written;
|
||||
return size;
|
||||
}
|
||||
|
||||
var done = false;
|
||||
while (true) {
|
||||
const inst = reader.readByte() catch |err| switch (err) {
|
||||
error.EndOfStream => break,
|
||||
else => return err,
|
||||
};
|
||||
const imm: u8 = inst & macho.BIND_IMMEDIATE_MASK;
|
||||
const opcode: u8 = inst & macho.BIND_OPCODE_MASK;
|
||||
pub fn writeLazyBindInfo(symbols: []*const ExternSymbol, writer: anytype) !void {
|
||||
for (symbols) |symbol| {
|
||||
try writer.writeByte(macho.BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB | @truncate(u4, symbol.segment));
|
||||
try leb.writeILEB128(writer, symbol.offset);
|
||||
|
||||
switch (opcode) {
|
||||
macho.BIND_OPCODE_DO_BIND => {
|
||||
try self.symbols.append(allocator, symbol);
|
||||
},
|
||||
macho.BIND_OPCODE_DONE => {
|
||||
done = true;
|
||||
symbol = .{
|
||||
.offset = 0,
|
||||
.segment = 0,
|
||||
.dylib_ordinal = 0,
|
||||
};
|
||||
},
|
||||
macho.BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM => {
|
||||
var name = std.ArrayList(u8).init(allocator);
|
||||
var next = try reader.readByte();
|
||||
while (next != @as(u8, 0)) {
|
||||
try name.append(next);
|
||||
next = try reader.readByte();
|
||||
}
|
||||
symbol.name = name.toOwnedSlice();
|
||||
},
|
||||
macho.BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB => {
|
||||
symbol.segment = imm;
|
||||
symbol.offset = try leb.readILEB128(i64, reader);
|
||||
},
|
||||
macho.BIND_OPCODE_SET_DYLIB_SPECIAL_IMM, macho.BIND_OPCODE_SET_DYLIB_ORDINAL_IMM => {
|
||||
symbol.dylib_ordinal = imm;
|
||||
},
|
||||
macho.BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB => {
|
||||
symbol.dylib_ordinal = try leb.readILEB128(i64, reader);
|
||||
},
|
||||
macho.BIND_OPCODE_SET_ADDEND_SLEB => {
|
||||
symbol.addend = try leb.readILEB128(i64, reader);
|
||||
},
|
||||
else => {
|
||||
std.log.warn("unhandled BIND_OPCODE_: 0x{x}", .{opcode});
|
||||
},
|
||||
}
|
||||
}
|
||||
assert(done);
|
||||
}
|
||||
|
||||
/// Write the binding info table to byte stream.
|
||||
pub fn write(self: LazyBindingInfoTable, writer: anytype) !void {
|
||||
for (self.symbols.items) |symbol| {
|
||||
try writer.writeByte(macho.BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB | @truncate(u4, symbol.segment));
|
||||
try leb.writeILEB128(writer, symbol.offset);
|
||||
|
||||
if (symbol.addend) |addend| {
|
||||
try writer.writeByte(macho.BIND_OPCODE_SET_ADDEND_SLEB);
|
||||
try leb.writeILEB128(writer, addend);
|
||||
}
|
||||
|
||||
if (symbol.dylib_ordinal > 15) {
|
||||
try writer.writeByte(macho.BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB);
|
||||
try leb.writeULEB128(writer, @bitCast(u64, symbol.dylib_ordinal));
|
||||
} else if (symbol.dylib_ordinal > 0) {
|
||||
try writer.writeByte(macho.BIND_OPCODE_SET_DYLIB_ORDINAL_IMM | @truncate(u4, @bitCast(u64, symbol.dylib_ordinal)));
|
||||
} else {
|
||||
try writer.writeByte(macho.BIND_OPCODE_SET_DYLIB_SPECIAL_IMM | @truncate(u4, @bitCast(u64, symbol.dylib_ordinal)));
|
||||
}
|
||||
|
||||
if (symbol.name) |name| {
|
||||
try writer.writeByte(macho.BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM); // TODO Sometimes we might want to add flags.
|
||||
try writer.writeAll(name);
|
||||
try writer.writeByte(0);
|
||||
}
|
||||
|
||||
try writer.writeByte(macho.BIND_OPCODE_DO_BIND);
|
||||
try writer.writeByte(macho.BIND_OPCODE_DONE);
|
||||
}
|
||||
}
|
||||
|
||||
/// Calculate size in bytes of this binding info table.
|
||||
pub fn calcSize(self: *LazyBindingInfoTable) !u64 {
|
||||
var stream = std.io.countingWriter(std.io.null_writer);
|
||||
var writer = stream.writer();
|
||||
var size: u64 = 0;
|
||||
|
||||
for (self.symbols.items) |symbol| {
|
||||
size += 1;
|
||||
try leb.writeILEB128(writer, symbol.offset);
|
||||
|
||||
if (symbol.addend) |addend| {
|
||||
size += 1;
|
||||
try leb.writeILEB128(writer, addend);
|
||||
}
|
||||
|
||||
size += 1;
|
||||
if (symbol.dylib_ordinal > 15) {
|
||||
try leb.writeULEB128(writer, @bitCast(u64, symbol.dylib_ordinal));
|
||||
}
|
||||
if (symbol.name) |name| {
|
||||
size += 1;
|
||||
size += name.len;
|
||||
size += 1;
|
||||
}
|
||||
size += 2;
|
||||
if (symbol.addend) |addend| {
|
||||
try writer.writeByte(macho.BIND_OPCODE_SET_ADDEND_SLEB);
|
||||
try leb.writeILEB128(writer, addend);
|
||||
}
|
||||
|
||||
size += stream.bytes_written;
|
||||
return size;
|
||||
if (symbol.dylib_ordinal > 15) {
|
||||
try writer.writeByte(macho.BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB);
|
||||
try leb.writeULEB128(writer, @bitCast(u64, symbol.dylib_ordinal));
|
||||
} else if (symbol.dylib_ordinal > 0) {
|
||||
try writer.writeByte(macho.BIND_OPCODE_SET_DYLIB_ORDINAL_IMM | @truncate(u4, @bitCast(u64, symbol.dylib_ordinal)));
|
||||
} else {
|
||||
try writer.writeByte(macho.BIND_OPCODE_SET_DYLIB_SPECIAL_IMM | @truncate(u4, @bitCast(u64, symbol.dylib_ordinal)));
|
||||
}
|
||||
|
||||
if (symbol.name) |name| {
|
||||
try writer.writeByte(macho.BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM); // TODO Sometimes we might want to add flags.
|
||||
try writer.writeAll(name);
|
||||
try writer.writeByte(0);
|
||||
}
|
||||
|
||||
try writer.writeByte(macho.BIND_OPCODE_DO_BIND);
|
||||
try writer.writeByte(macho.BIND_OPCODE_DONE);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user