macho: save lazy binding info as part of the atom

This commit is contained in:
Jakub Konka 2021-08-26 11:43:11 +02:00
parent c12183b608
commit d19d3342c2
3 changed files with 126 additions and 92 deletions

View File

@ -155,9 +155,7 @@ strtab: std.ArrayListUnmanaged(u8) = .{},
strtab_dir: std.HashMapUnmanaged(u32, u32, StringIndexContext, std.hash_map.default_max_load_percentage) = .{},
got_entries_map: std.AutoArrayHashMapUnmanaged(GotIndirectionKey, *TextBlock) = .{},
stubs: std.ArrayListUnmanaged(u32) = .{},
stubs_map: std.AutoHashMapUnmanaged(u32, u32) = .{},
stubs_map: std.AutoArrayHashMapUnmanaged(u32, *TextBlock) = .{},
error_flags: File.ErrorFlags = File.ErrorFlags{},
@ -783,27 +781,6 @@ pub fn flush(self: *MachO, comp: *Compilation) !void {
try self.parseTextBlocks();
try self.sortSections();
for (self.stubs.items) |_| {
const stub_helper_atom = try self.createStubHelperAtom();
try self.allocateAtomStage1(stub_helper_atom, .{
.seg = self.text_segment_cmd_index.?,
.sect = self.stub_helper_section_index.?,
});
const laptr_atom = try self.createLazyPointerAtom(stub_helper_atom.local_sym_index);
try self.allocateAtomStage1(laptr_atom, .{
.seg = self.data_segment_cmd_index.?,
.sect = self.la_symbol_ptr_section_index.?,
});
const stub_atom = try self.createStubAtom(laptr_atom.local_sym_index);
try self.allocateAtomStage1(stub_atom, .{
.seg = self.text_segment_cmd_index.?,
.sect = self.stubs_section_index.?,
});
}
try self.allocateTextSegment();
try self.allocateDataConstSegment();
try self.allocateDataSegment();
@ -2159,7 +2136,7 @@ fn createStubHelperPreambleAtom(self: *MachO) !*TextBlock {
return atom;
}
fn createStubHelperAtom(self: *MachO) !*TextBlock {
pub fn createStubHelperAtom(self: *MachO) !*TextBlock {
const arch = self.base.options.target.cpu.arch;
const stub_size: u4 = switch (arch) {
.x86_64 => 10,
@ -2225,7 +2202,7 @@ fn createStubHelperAtom(self: *MachO) !*TextBlock {
return atom;
}
fn createLazyPointerAtom(self: *MachO, stub_sym_index: u32) !*TextBlock {
pub fn createLazyPointerAtom(self: *MachO, stub_sym_index: u32, lazy_binding_sym_index: u32) !*TextBlock {
const local_sym_index = @intCast(u32, self.locals.items.len);
try self.locals.append(self.base.allocator, .{
.n_strx = try self.makeString("lazy_ptr"),
@ -2248,11 +2225,15 @@ fn createLazyPointerAtom(self: *MachO, stub_sym_index: u32) !*TextBlock {
},
});
try atom.rebases.append(self.base.allocator, 0);
try atom.lazy_bindings.append(self.base.allocator, .{
.local_sym_index = lazy_binding_sym_index,
.offset = 0,
});
self.lazy_binding_info_dirty = true;
return atom;
}
fn createStubAtom(self: *MachO, laptr_sym_index: u32) !*TextBlock {
pub fn createStubAtom(self: *MachO, laptr_sym_index: u32) !*TextBlock {
const arch = self.base.options.target.cpu.arch;
const alignment: u2 = switch (arch) {
.x86_64 => 0,
@ -2661,7 +2642,10 @@ fn resolveSymbols(self: *MachO) !void {
break :blk atom;
};
const laptr_atom = blk: {
const atom = try self.createLazyPointerAtom(stub_helper_atom.local_sym_index);
const atom = try self.createLazyPointerAtom(
stub_helper_atom.local_sym_index,
resolv.where_index,
);
const match = MatchingSection{
.seg = self.data_segment_cmd_index.?,
.sect = self.la_symbol_ptr_section_index.?,
@ -2990,8 +2974,6 @@ fn writeRebaseInfoTableZld(self: *MachO) !void {
}
}
std.sort.sort(bind.Pointer, pointers.items, {}, bind.pointerCmp);
const size = try bind.rebaseInfoSize(pointers.items);
var buffer = try self.base.allocator.alloc(u8, @intCast(usize, size));
defer self.base.allocator.free(buffer);
@ -3067,22 +3049,29 @@ fn writeLazyBindInfoTableZld(self: *MachO) !void {
var pointers = std.ArrayList(bind.Pointer).init(self.base.allocator);
defer pointers.deinit();
if (self.la_symbol_ptr_section_index) |idx| {
if (self.la_symbol_ptr_section_index) |sect| blk: {
var atom = self.blocks.get(.{
.seg = self.data_segment_cmd_index.?,
.sect = sect,
}) orelse break :blk;
const seg = self.load_commands.items[self.data_segment_cmd_index.?].Segment;
const sect = seg.sections.items[idx];
const base_offset = sect.addr - seg.inner.vmaddr;
const segment_id = @intCast(u16, self.data_segment_cmd_index.?);
try pointers.ensureUnusedCapacity(self.stubs.items.len);
while (true) {
const sym = self.locals.items[atom.local_sym_index];
const base_offset = sym.n_value - seg.inner.vmaddr;
for (self.stubs.items) |import_id, i| {
const sym = self.undefs.items[import_id];
pointers.appendAssumeCapacity(.{
.offset = base_offset + i * @sizeOf(u64),
.segment_id = segment_id,
.dylib_ordinal = @divExact(sym.n_desc, macho.N_SYMBOL_RESOLVER),
.name = self.getString(sym.n_strx),
});
for (atom.lazy_bindings.items) |binding| {
const bind_sym = self.undefs.items[binding.local_sym_index];
try pointers.append(.{
.offset = binding.offset + base_offset,
.segment_id = self.data_segment_cmd_index.?,
.dylib_ordinal = @divExact(bind_sym.n_desc, macho.N_SYMBOL_RESOLVER),
.name = self.getString(bind_sym.n_strx),
});
}
if (atom.prev) |prev| {
atom = prev;
} else break;
}
}
@ -3245,7 +3234,7 @@ fn writeSymbolTable(self: *MachO) !void {
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 nstubs = @intCast(u32, self.stubs.items.len);
const nstubs = @intCast(u32, self.stubs_map.keys().len);
const ngot_entries = @intCast(u32, self.got_entries_map.keys().len);
dysymtab.indirectsymoff = @intCast(u32, seg.inner.fileoff + seg.inner.filesize);
@ -3266,8 +3255,8 @@ fn writeSymbolTable(self: *MachO) !void {
var writer = stream.writer();
stubs.reserved1 = 0;
for (self.stubs.items) |id| {
try writer.writeIntLittle(u32, dysymtab.iundefsym + id);
for (self.stubs_map.keys()) |key| {
try writer.writeIntLittle(u32, dysymtab.iundefsym + key);
}
got.reserved1 = nstubs;
@ -3283,8 +3272,8 @@ fn writeSymbolTable(self: *MachO) !void {
}
la_symbol_ptr.reserved1 = got.reserved1 + ngot_entries;
for (self.stubs.items) |id| {
try writer.writeIntLittle(u32, dysymtab.iundefsym + id);
for (self.stubs_map.keys()) |key| {
try writer.writeIntLittle(u32, dysymtab.iundefsym + key);
}
try self.base.file.?.pwriteAll(buf, dysymtab.indirectsymoff);
@ -3301,7 +3290,6 @@ pub fn deinit(self: *MachO) void {
self.section_ordinals.deinit(self.base.allocator);
self.got_entries_map.deinit(self.base.allocator);
self.stubs.deinit(self.base.allocator);
self.stubs_map.deinit(self.base.allocator);
self.strtab_dir.deinit(self.base.allocator);
self.strtab.deinit(self.base.allocator);
@ -4557,10 +4545,6 @@ pub fn addExternFn(self: *MachO, name: []const u8) !u32 {
});
try self.unresolved.putNoClobber(self.base.allocator, sym_index, .stub);
const stubs_index = @intCast(u32, self.stubs.items.len);
try self.stubs.append(self.base.allocator, sym_index);
try self.stubs_map.putNoClobber(self.base.allocator, sym_index, stubs_index);
return sym_index;
}
@ -4803,7 +4787,7 @@ fn writeIndirectSymbolTable(self: *MachO) !void {
const la_symbol_ptr = &data_segment.sections.items[self.la_symbol_ptr_section_index.?];
const dysymtab = &self.load_commands.items[self.dysymtab_cmd_index.?].Dysymtab;
const nstubs = @intCast(u32, self.stubs.items.len);
const nstubs = @intCast(u32, self.stubs_map.keys().len);
const ngot_entries = @intCast(u32, self.got_entries_map.keys().len);
const allocated_size = self.allocatedSizeLinkedit(dysymtab.indirectsymoff);
const nindirectsyms = nstubs * 2 + ngot_entries;
@ -4825,8 +4809,8 @@ fn writeIndirectSymbolTable(self: *MachO) !void {
var writer = stream.writer();
stubs.reserved1 = 0;
for (self.stubs.items) |id| {
try writer.writeIntLittle(u32, dysymtab.iundefsym + id);
for (self.stubs_map.keys()) |key| {
try writer.writeIntLittle(u32, dysymtab.iundefsym + key);
}
got.reserved1 = nstubs;
@ -4842,8 +4826,8 @@ fn writeIndirectSymbolTable(self: *MachO) !void {
}
la_symbol_ptr.reserved1 = got.reserved1 + ngot_entries;
for (self.stubs.items) |id| {
try writer.writeIntLittle(u32, dysymtab.iundefsym + id);
for (self.stubs_map.keys()) |key| {
try writer.writeIntLittle(u32, dysymtab.iundefsym + key);
}
try self.base.file.?.pwriteAll(buf, dysymtab.indirectsymoff);
@ -5050,8 +5034,6 @@ fn writeRebaseInfoTable(self: *MachO) !void {
}
}
std.sort.sort(bind.Pointer, pointers.items, {}, bind.pointerCmp);
const size = try bind.rebaseInfoSize(pointers.items);
var buffer = try self.base.allocator.alloc(u8, @intCast(usize, size));
defer self.base.allocator.free(buffer);
@ -5151,22 +5133,29 @@ fn writeLazyBindInfoTable(self: *MachO) !void {
var pointers = std.ArrayList(bind.Pointer).init(self.base.allocator);
defer pointers.deinit();
if (self.la_symbol_ptr_section_index) |idx| {
if (self.la_symbol_ptr_section_index) |sect| blk: {
var atom = self.blocks.get(.{
.seg = self.data_segment_cmd_index.?,
.sect = sect,
}) orelse break :blk;
const seg = self.load_commands.items[self.data_segment_cmd_index.?].Segment;
const sect = seg.sections.items[idx];
const base_offset = sect.addr - seg.inner.vmaddr;
const segment_id = @intCast(u16, self.data_segment_cmd_index.?);
try pointers.ensureUnusedCapacity(self.stubs.items.len);
while (true) {
const sym = self.locals.items[atom.local_sym_index];
const base_offset = sym.n_value - seg.inner.vmaddr;
for (self.stubs.items) |import_id, i| {
const sym = self.undefs.items[import_id];
pointers.appendAssumeCapacity(.{
.offset = base_offset + i * @sizeOf(u64),
.segment_id = segment_id,
.dylib_ordinal = @divExact(sym.n_desc, macho.N_SYMBOL_RESOLVER),
.name = self.getString(sym.n_strx),
});
for (atom.lazy_bindings.items) |binding| {
const bind_sym = self.undefs.items[binding.local_sym_index];
try pointers.append(.{
.offset = binding.offset + base_offset,
.segment_id = self.data_segment_cmd_index.?,
.dylib_ordinal = @divExact(bind_sym.n_desc, macho.N_SYMBOL_RESOLVER),
.name = self.getString(bind_sym.n_strx),
});
}
if (atom.prev) |prev| {
atom = prev;
} else break;
}
}
@ -5203,6 +5192,15 @@ fn populateLazyBindOffsetsInStubHelper(self: *MachO, buffer: []const u8) !void {
}) orelse return;
if (last_atom.local_sym_index == self.stub_preamble_sym_index.?) return;
// Because we insert lazy binding opcodes in reverse order (from last to the first atom),
// we need reverse the order of atom traversal here as well.
// TODO figure out a less error prone mechanims for this!
var atom = last_atom;
while (atom.prev) |prev| {
atom = prev;
}
atom = atom.next.?;
var stream = std.io.fixedBufferStream(buffer);
var reader = stream.reader();
var offsets = std.ArrayList(u32).init(self.base.allocator);
@ -5255,7 +5253,6 @@ fn populateLazyBindOffsetsInStubHelper(self: *MachO, buffer: []const u8) !void {
else => unreachable,
};
var buf: [@sizeOf(u32)]u8 = undefined;
var atom = last_atom;
_ = offsets.pop();
while (offsets.popOrNull()) |bind_offset| {
const sym = self.locals.items[atom.local_sym_index];
@ -5268,8 +5265,8 @@ fn populateLazyBindOffsetsInStubHelper(self: *MachO, buffer: []const u8) !void {
});
try self.base.file.?.pwriteAll(&buf, file_offset);
if (atom.prev) |prev| {
atom = prev;
if (atom.next) |next| {
atom = next;
} else break;
}
}

View File

@ -50,6 +50,9 @@ rebases: std.ArrayListUnmanaged(u64) = .{},
/// symbols (aka proxies aka imports)
bindings: std.ArrayListUnmanaged(SymbolAtOffset) = .{},
/// List of lazy bindings
lazy_bindings: std.ArrayListUnmanaged(SymbolAtOffset) = .{},
/// List of data-in-code entries. This is currently specific to x86_64 only.
dices: std.ArrayListUnmanaged(macho.data_in_code_entry) = .{},
@ -570,6 +573,7 @@ pub const empty = TextBlock{
pub fn deinit(self: *TextBlock, allocator: *Allocator) void {
self.dices.deinit(allocator);
self.lazy_bindings.deinit(allocator);
self.bindings.deinit(allocator);
self.rebases.deinit(allocator);
self.relocs.deinit(allocator);
@ -898,9 +902,53 @@ pub fn parseRelocs(self: *TextBlock, relocs: []macho.relocation_info, context: R
if (parsed_rel.where != .undef) break :blk;
if (context.macho_file.stubs_map.contains(parsed_rel.where_index)) break :blk;
const stubs_index = @intCast(u32, context.macho_file.stubs.items.len);
try context.macho_file.stubs.append(context.allocator, parsed_rel.where_index);
try context.macho_file.stubs_map.putNoClobber(context.allocator, parsed_rel.where_index, stubs_index);
const stub_helper_atom = try context.macho_file.createStubHelperAtom();
const laptr_atom = try context.macho_file.createLazyPointerAtom(
stub_helper_atom.local_sym_index,
parsed_rel.where_index,
);
const stub_atom = try context.macho_file.createStubAtom(laptr_atom.local_sym_index);
try context.macho_file.stubs_map.putNoClobber(context.allocator, parsed_rel.where_index, stub_atom);
if (build_options.is_stage1 and context.macho_file.base.options.use_stage1) {
try context.macho_file.allocateAtomStage1(stub_helper_atom, .{
.seg = context.macho_file.text_segment_cmd_index.?,
.sect = context.macho_file.stub_helper_section_index.?,
});
try context.macho_file.allocateAtomStage1(laptr_atom, .{
.seg = context.macho_file.data_segment_cmd_index.?,
.sect = context.macho_file.la_symbol_ptr_section_index.?,
});
try context.macho_file.allocateAtomStage1(stub_atom, .{
.seg = context.macho_file.text_segment_cmd_index.?,
.sect = context.macho_file.stubs_section_index.?,
});
} else {
{
const match = MachO.MatchingSection{
.seg = context.macho_file.text_segment_cmd_index.?,
.sect = context.macho_file.stub_helper_section_index.?,
};
_ = try context.macho_file.allocateAtom(stub_helper_atom, match);
try context.macho_file.writeAtom(stub_helper_atom, match);
}
{
const match = MachO.MatchingSection{
.seg = context.macho_file.data_segment_cmd_index.?,
.sect = context.macho_file.la_symbol_ptr_section_index.?,
};
_ = try context.macho_file.allocateAtom(laptr_atom, match);
try context.macho_file.writeAtom(laptr_atom, match);
}
{
const match = MachO.MatchingSection{
.seg = context.macho_file.text_segment_cmd_index.?,
.sect = context.macho_file.stubs_section_index.?,
};
_ = try context.macho_file.allocateAtom(stub_atom, match);
try context.macho_file.writeAtom(stub_atom, match);
}
}
}
}
}
@ -1145,13 +1193,11 @@ pub fn resolveRelocs(self: *TextBlock, macho_file: *MachO) !void {
break :blk sym.n_value;
},
.undef => {
const stubs_index = macho_file.stubs_map.get(rel.where_index) orelse {
const atom = macho_file.stubs_map.get(rel.where_index) orelse {
// TODO verify in TextBlock that the symbol is indeed dynamically bound.
break :blk 0; // Dynamically bound by dyld.
};
const segment = macho_file.load_commands.items[macho_file.text_segment_cmd_index.?].Segment;
const stubs = segment.sections.items[macho_file.stubs_section_index.?];
break :blk stubs.addr + stubs_index * stubs.reserved2;
break :blk macho_file.locals.items[atom.local_sym_index].n_value;
},
}
};

View File

@ -9,15 +9,6 @@ pub const Pointer = struct {
name: ?[]const u8 = null,
};
pub fn pointerCmp(context: void, a: Pointer, b: Pointer) bool {
_ = context;
if (a.segment_id < b.segment_id) return true;
if (a.segment_id == b.segment_id) {
return a.offset < b.offset;
}
return false;
}
pub fn rebaseInfoSize(pointers: []const Pointer) !u64 {
var stream = std.io.countingWriter(std.io.null_writer);
var writer = stream.writer();