Commit d19d3342c2
Changed files (3)
src
link
src/link/MachO/bind.zig
@@ -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();
src/link/MachO/TextBlock.zig
@@ -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;
},
}
};
src/link/MachO.zig
@@ -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);
-
- 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),
- });
+
+ while (true) {
+ const sym = self.locals.items[atom.local_sym_index];
+ const base_offset = sym.n_value - seg.inner.vmaddr;
+
+ 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);
-
- 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),
- });
+
+ while (true) {
+ const sym = self.locals.items[atom.local_sym_index];
+ const base_offset = sym.n_value - seg.inner.vmaddr;
+
+ 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;
}
}