aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJakub Konka <kubkon@jakubkonka.com>2021-08-23 22:12:04 +0200
committerJakub Konka <kubkon@jakubkonka.com>2021-08-23 22:12:04 +0200
commit799c5bb9551dafd76f9d1fce7d3f5a01ac55da83 (patch)
tree2951228b30769bddbacee40faba6cf4230ba54ef
parentea499203fe22dd3cd40753c020794790f32b91be (diff)
downloadzig-799c5bb9551dafd76f9d1fce7d3f5a01ac55da83.tar.gz
zig-799c5bb9551dafd76f9d1fce7d3f5a01ac55da83.zip
macho: add routine for creating a dynamic stub_helper atom
With this routine, we are now able to freely shift stub_helper section in memory and in file since the VM addressing is now dynamically dependent on the positioning of `__stub_helper` preamble and other sections generated by the linker.
-rw-r--r--src/link/MachO.zig347
1 files changed, 173 insertions, 174 deletions
diff --git a/src/link/MachO.zig b/src/link/MachO.zig
index fe7c7789bf..6785da065b 100644
--- a/src/link/MachO.zig
+++ b/src/link/MachO.zig
@@ -177,8 +177,6 @@ has_stabs: bool = false,
section_ordinals: std.AutoArrayHashMapUnmanaged(MatchingSection, void) = .{},
-pending_updates: std.ArrayListUnmanaged(PendingUpdate) = .{},
-
/// A list of text blocks that have surplus capacity. This list can have false
/// positives, as functions grow and shrink over time, only sometimes being added
/// or removed from the freelist.
@@ -384,6 +382,26 @@ pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Optio
try ds.writeLocalSymbol(0);
}
+ {
+ const atom = try self.createDyldPrivateAtom();
+ const match = MatchingSection{
+ .seg = self.data_segment_cmd_index.?,
+ .sect = self.data_section_index.?,
+ };
+ const vaddr = try self.allocateAtom(atom, match);
+ try self.writeAtom(atom, match);
+ }
+
+ {
+ const atom = try self.createStubHelperPreambleAtom();
+ const match = MatchingSection{
+ .seg = self.text_segment_cmd_index.?,
+ .sect = self.stub_helper_section_index.?,
+ };
+ const vaddr = try self.allocateAtom(atom, match);
+ try self.writeAtom(atom, match);
+ }
+
return self;
}
@@ -760,50 +778,42 @@ pub fn flush(self: *MachO, comp: *Compilation) !void {
try self.parseLibs(libs.items, self.base.options.sysroot);
try self.resolveSymbols();
try self.resolveDyldStubBinder();
- try self.createDyldPrivateAtom();
- try self.createStubHelperPreambleAtom();
-
- if (!use_stage1) {
- // TODO just a temp
- const seg = self.load_commands.items[self.text_segment_cmd_index.?].Segment;
- const sect = seg.sections.items[self.stub_helper_section_index.?];
- self.stub_helper_stubs_start_off = sect.offset + switch (self.base.options.target.cpu.arch) {
- .x86_64 => @intCast(u64, 15),
- .aarch64 => @intCast(u64, 6 * @sizeOf(u32)),
- else => unreachable,
- };
- }
-
- // Apply pending updates
- var still_pending = std.ArrayList(PendingUpdate).init(self.base.allocator);
- defer still_pending.deinit();
-
- for (self.pending_updates.items) |update| {
- switch (update) {
- .add_stub_entry => |stub_index| {
- try self.writeStub(stub_index);
- try self.writeStubInStubHelper(stub_index);
- try self.writeLazySymbolPointer(stub_index);
- self.rebase_info_dirty = true;
- self.lazy_binding_info_dirty = true;
- },
- else => unreachable,
- }
- }
-
- self.pending_updates.clearRetainingCapacity();
- for (still_pending.items) |update| {
- self.pending_updates.appendAssumeCapacity(update);
- }
-
- try self.parseTextBlocks();
try self.addRpathLCs(rpath_table.keys());
try self.addLoadDylibLCs();
try self.addDataInCodeLC();
try self.addCodeSignatureLC();
if (use_stage1) {
+ try self.parseTextBlocks();
try self.sortSections();
+ {
+ const atom = try self.createDyldPrivateAtom();
+ try self.allocateAtomStage1(atom, .{
+ .seg = self.data_segment_cmd_index.?,
+ .sect = self.data_section_index.?,
+ });
+ }
+ {
+ const atom = try self.createStubHelperPreambleAtom();
+ try self.allocateAtomStage1(atom, .{
+ .seg = self.text_segment_cmd_index.?,
+ .sect = self.stub_helper_section_index.?,
+ });
+
+ // TODO this is just a temp
+ // We already prealloc stub helper size in populateMissingMetadata(), but
+ // perhaps it's not needed after all?
+ const seg = &self.load_commands.items[self.text_segment_cmd_index.?].Segment;
+ const sect = &seg.sections.items[self.stub_helper_section_index.?];
+ sect.size -= atom.size;
+ }
+ for (self.stubs.items) |_| {
+ const atom = try self.createStubHelperAtom();
+ try self.allocateAtomStage1(atom, .{
+ .seg = self.text_segment_cmd_index.?,
+ .sect = self.stub_helper_section_index.?,
+ });
+ }
try self.allocateTextSegment();
try self.allocateDataConstSegment();
try self.allocateDataSegment();
@@ -1705,18 +1715,10 @@ fn allocateTextSegment(self: *MachO) !void {
seg.inner.fileoff = 0;
seg.inner.vmaddr = base_vmaddr;
- // Set stubs and stub_helper sizes
+ // Set stubs sizes
const stubs = &seg.sections.items[self.stubs_section_index.?];
- const stub_helper = &seg.sections.items[self.stub_helper_section_index.?];
stubs.size += nstubs * stubs.reserved2;
- const stub_size: u4 = switch (self.base.options.target.cpu.arch) {
- .x86_64 => 10,
- .aarch64 => 3 * @sizeOf(u32),
- else => unreachable,
- };
- stub_helper.size += nstubs * stub_size;
-
var sizeofcmds: u64 = 0;
for (self.load_commands.items) |lc| {
sizeofcmds += lc.cmdsize();
@@ -1947,41 +1949,81 @@ fn createEmptyAtom(
defer self.base.allocator.free(code);
mem.set(u8, code, 0);
- const block = try self.base.allocator.create(TextBlock);
- errdefer self.base.allocator.destroy(block);
- block.* = TextBlock.empty;
- block.local_sym_index = local_sym_index;
- block.size = size;
- block.alignment = alignment;
- try block.code.appendSlice(self.base.allocator, code);
+ const atom = try self.base.allocator.create(TextBlock);
+ errdefer self.base.allocator.destroy(atom);
+ atom.* = TextBlock.empty;
+ atom.local_sym_index = local_sym_index;
+ atom.size = size;
+ atom.alignment = alignment;
+ try atom.code.appendSlice(self.base.allocator, code);
+ try self.managed_blocks.append(self.base.allocator, atom);
- try self.managed_blocks.append(self.base.allocator, block);
+ return atom;
+}
+
+fn allocateAtom(self: *MachO, atom: *TextBlock, match: MatchingSection) !u64 {
+ // TODO converge with `allocateTextBlock`
+ const seg = self.load_commands.items[match.seg].Segment;
+ const sect = seg.sections.items[match.sect];
+ const base_addr = if (atom.prev) |prev| blk: {
+ const prev_atom_sym = self.locals.items[prev.local_sym_index];
+ break :blk prev_atom_sym.n_value;
+ } else sect.addr;
+ const atom_alignment = try math.powi(u32, 2, atom.alignment);
+ const vaddr = mem.alignForwardGeneric(u64, base_addr, atom_alignment);
+
+ // TODO we should check if we need to expand the section or not like we
+ // do in `allocateTextBlock`.
+ if (self.blocks.getPtr(match)) |last| {
+ last.*.next = atom;
+ atom.prev = last.*;
+ last.* = atom;
+ } else {
+ try self.blocks.putNoClobber(self.base.allocator, match, atom);
+ }
+ return vaddr;
+}
+
+fn writeAtom(self: *MachO, atom: *TextBlock, match: MatchingSection) !void {
+ const seg = self.load_commands.items[match.seg].Segment;
+ const sect = seg.sections.items[match.sect];
+
+ const vaddr = try self.allocateAtom(atom, match);
+ const sym = &self.locals.items[atom.local_sym_index];
+ sym.n_value = vaddr;
+ sym.n_sect = @intCast(u8, self.section_ordinals.getIndex(match).? + 1);
+
+ try atom.resolveRelocs(self);
+
+ const file_offset = sect.offset + vaddr - sect.addr;
+ log.debug("writing atom for symbol {s} at file offset 0x{x}", .{ self.getString(sym.n_strx), file_offset });
+ try self.base.file.?.pwriteAll(atom.code.items, file_offset);
+ try self.writeLocalSymbol(atom.local_sym_index);
+}
+
+fn allocateAtomStage1(self: *MachO, atom: *TextBlock, match: MatchingSection) !void {
// Update target section's metadata
// TODO should we update segment's size here too?
// How does it tie with incremental space allocs?
const tseg = &self.load_commands.items[match.seg].Segment;
const tsect = &tseg.sections.items[match.sect];
- const new_alignment = math.max(tsect.@"align", alignment);
+ const new_alignment = math.max(tsect.@"align", atom.alignment);
const new_alignment_pow_2 = try math.powi(u32, 2, new_alignment);
- const new_size = mem.alignForwardGeneric(u64, tsect.size, new_alignment_pow_2) + size;
+ const new_size = mem.alignForwardGeneric(u64, tsect.size, new_alignment_pow_2) + atom.size;
tsect.size = new_size;
tsect.@"align" = new_alignment;
if (self.blocks.getPtr(match)) |last| {
- last.*.next = block;
- block.prev = last.*;
- last.* = block;
+ last.*.next = atom;
+ atom.prev = last.*;
+ last.* = atom;
} else {
- try self.blocks.putNoClobber(self.base.allocator, match, block);
+ try self.blocks.putNoClobber(self.base.allocator, match, atom);
}
-
- return block;
}
-fn createDyldPrivateAtom(self: *MachO) !void {
- if (self.dyld_private_sym_index != null) return;
-
+fn createDyldPrivateAtom(self: *MachO) !*TextBlock {
const match = MatchingSection{
.seg = self.data_segment_cmd_index.?,
.sect = self.data_section_index.?,
@@ -1994,35 +2036,11 @@ fn createDyldPrivateAtom(self: *MachO) !void {
.n_desc = 0,
.n_value = 0,
});
- const last = self.blocks.get(match);
- const atom = try self.createEmptyAtom(match, local_sym_index, @sizeOf(u64), 3);
-
- if (!(build_options.is_stage1 and self.base.options.use_stage1)) {
- const seg = self.load_commands.items[match.seg].Segment;
- const sect = seg.sections.items[match.sect];
- const base_addr = if (last) |last_atom| blk: {
- const last_atom_sym = self.locals.items[last_atom.local_sym_index];
- break :blk last_atom_sym.n_value;
- } else sect.addr;
- const n_sect = @intCast(u8, self.section_ordinals.getIndex(match).? + 1);
- const atom_alignment = try math.powi(u32, 2, atom.alignment);
- const vaddr = mem.alignForwardGeneric(u64, base_addr, atom_alignment);
-
- const sym = &self.locals.items[local_sym_index];
- sym.n_value = vaddr;
- sym.n_sect = n_sect;
-
- const file_offset = sect.offset + vaddr - sect.addr;
- log.debug("writing code for symbol {s} at file offset 0x{x}", .{ self.getString(sym.n_strx), file_offset });
- try self.base.file.?.pwriteAll(atom.code.items, file_offset);
- try self.writeLocalSymbol(local_sym_index);
- }
-
self.dyld_private_sym_index = local_sym_index;
+ return self.createEmptyAtom(match, local_sym_index, @sizeOf(u64), 3);
}
-fn createStubHelperPreambleAtom(self: *MachO) !void {
- if (self.stub_preamble_sym_index != null) return;
+fn createStubHelperPreambleAtom(self: *MachO) !*TextBlock {
const arch = self.base.options.target.cpu.arch;
const match = MatchingSection{
.seg = self.text_segment_cmd_index.?,
@@ -2046,7 +2064,6 @@ fn createStubHelperPreambleAtom(self: *MachO) !void {
.n_desc = 0,
.n_value = 0,
});
- const last = self.blocks.get(match);
const atom = try self.createEmptyAtom(match, local_sym_index, size, alignment);
switch (arch) {
.x86_64 => {
@@ -2157,36 +2174,71 @@ fn createStubHelperPreambleAtom(self: *MachO) !void {
else => unreachable,
}
self.stub_preamble_sym_index = local_sym_index;
+ return atom;
+}
- if (!(build_options.is_stage1 and self.base.options.use_stage1)) {
- const seg = self.load_commands.items[match.seg].Segment;
- const sect = seg.sections.items[match.sect];
- const base_addr = if (last) |last_atom| blk: {
- const last_atom_sym = self.locals.items[last_atom.local_sym_index];
- break :blk last_atom_sym.n_value;
- } else sect.addr;
- const n_sect = @intCast(u8, self.section_ordinals.getIndex(match).? + 1);
- const atom_alignment = try math.powi(u32, 2, atom.alignment);
- const vaddr = mem.alignForwardGeneric(u64, base_addr, atom_alignment);
-
- const sym = &self.locals.items[local_sym_index];
- sym.n_value = vaddr;
- sym.n_sect = n_sect;
-
- try atom.resolveRelocs(self);
+fn createStubHelperAtom(self: *MachO) !*TextBlock {
+ const arch = self.base.options.target.cpu.arch;
+ const stub_size: u4 = switch (arch) {
+ .x86_64 => 10,
+ .aarch64 => 3 * @sizeOf(u32),
+ else => unreachable,
+ };
+ const local_sym_index = @intCast(u32, self.locals.items.len);
+ try self.locals.append(self.base.allocator, .{
+ .n_strx = try self.makeString("stub_in_stub_helper"),
+ .n_type = macho.N_SECT,
+ .n_sect = 0,
+ .n_desc = 0,
+ .n_value = 0,
+ });
+ const atom = try self.createEmptyAtom(.{
+ .seg = self.text_segment_cmd_index.?,
+ .sect = self.stub_helper_section_index.?,
+ }, local_sym_index, stub_size, 2);
+ try atom.relocs.ensureTotalCapacity(self.base.allocator, 1);
- const file_offset = sect.offset + vaddr - sect.addr;
- log.debug("writing code for symbol {s} at file offset 0x{x}", .{ self.getString(sym.n_strx), file_offset });
- try self.base.file.?.pwriteAll(atom.code.items, file_offset);
- try self.writeLocalSymbol(local_sym_index);
+ switch (arch) {
+ .x86_64 => {
+ // pushq
+ atom.code.items[0] = 0x68;
+ // Next 4 bytes 1..4 are just a placeholder populated in `populateLazyBindOffsetsInStubHelper`.
+ // jmpq
+ atom.code.items[5] = 0xe9;
+ atom.relocs.appendAssumeCapacity(.{
+ .offset = 6,
+ .where = .local,
+ .where_index = self.stub_preamble_sym_index.?,
+ .payload = .{
+ .branch = .{ .arch = arch },
+ },
+ });
+ },
+ .aarch64 => {
+ const literal = blk: {
+ const div_res = try math.divExact(u64, stub_size - @sizeOf(u32), 4);
+ break :blk try math.cast(u18, div_res);
+ };
+ // ldr w16, literal
+ mem.writeIntLittle(u32, atom.code.items[0..4], aarch64.Instruction.ldr(.w16, .{
+ .literal = literal,
+ }).toU32());
+ // b disp
+ mem.writeIntLittle(u32, atom.code.items[4..8], aarch64.Instruction.b(0).toU32());
+ atom.relocs.appendAssumeCapacity(.{
+ .offset = 4,
+ .where = .local,
+ .where_index = self.stub_preamble_sym_index.?,
+ .payload = .{
+ .branch = .{ .arch = arch },
+ },
+ });
+ // Next 4 bytes 8..12 are just a placeholder populated in `populateLazyBindOffsetsInStubHelper`.
+ },
+ else => unreachable,
}
- // TODO this needs to be fixed
- // We already prealloc stub helper size in populateMissingMetadata(), but
- // perhaps it's not needed after all?
- const seg = &self.load_commands.items[self.text_segment_cmd_index.?].Segment;
- const sect = &seg.sections.items[self.stub_helper_section_index.?];
- sect.size -= size;
+ return atom;
}
fn resolveSymbolsInObject(
@@ -2681,7 +2733,6 @@ fn flushZld(self: *MachO) !void {
// TODO weak bound pointers
try self.writeLazySymbolPointer(index);
try self.writeStub(index);
- try self.writeStubInStubHelper(index);
}
if (self.common_section_index) |index| {
@@ -3173,7 +3224,6 @@ pub fn deinit(self: *MachO) void {
}
self.section_ordinals.deinit(self.base.allocator);
- self.pending_updates.deinit(self.base.allocator);
self.got_entries.deinit(self.base.allocator);
self.got_entries_map.deinit(self.base.allocator);
self.got_entries_free_list.deinit(self.base.allocator);
@@ -4404,11 +4454,7 @@ pub fn addExternFn(self: *MachO, name: []const u8) !u32 {
try self.stubs.append(self.base.allocator, sym_index);
try self.stubs_map.putNoClobber(self.base.allocator, sym_index, stubs_index);
- // TODO discuss this. The caller context expects codegen.InnerError{ OutOfMemory, CodegenFail },
- // which obviously doesn't include file writing op errors. So instead of trying to write the stub
- // entry right here and now, queue it up and dispose of when updating decl.
- try self.pending_updates.ensureUnusedCapacity(self.base.allocator, 1);
- self.pending_updates.appendAssumeCapacity(.{ .add_stub_entry = stubs_index });
+ // TODO create and write stub, stub_helper and lazy_ptr atoms
return sym_index;
}
@@ -4683,53 +4729,6 @@ fn writeStub(self: *MachO, index: u32) !void {
try self.base.file.?.pwriteAll(code, stub_off);
}
-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_size: u4 = switch (self.base.options.target.cpu.arch) {
- .x86_64 => 10,
- .aarch64 => 3 * @sizeOf(u32),
- else => unreachable,
- };
- const stub_off = self.stub_helper_stubs_start_off.? + index * stub_size;
-
- var code = try self.base.allocator.alloc(u8, stub_size);
- defer self.base.allocator.free(code);
-
- switch (self.base.options.target.cpu.arch) {
- .x86_64 => {
- const displacement = try math.cast(
- i32,
- @intCast(i64, stub_helper.offset) - @intCast(i64, stub_off) - stub_size,
- );
- // pushq
- code[0] = 0x68;
- mem.writeIntLittle(u32, code[1..][0..4], 0x0); // Just a placeholder populated in `populateLazyBindOffsetsInStubHelper`.
- // jmpq
- code[5] = 0xe9;
- mem.writeIntLittle(u32, code[6..][0..4], @bitCast(u32, displacement));
- },
- .aarch64 => {
- const literal = blk: {
- const div_res = try math.divExact(u64, stub_size - @sizeOf(u32), 4);
- break :blk try math.cast(u18, div_res);
- };
- // ldr w16, literal
- mem.writeIntLittle(u32, code[0..4], aarch64.Instruction.ldr(.w16, .{
- .literal = literal,
- }).toU32());
- const displacement = try math.cast(i28, @intCast(i64, stub_helper.offset) - @intCast(i64, stub_off) - 4);
- // b disp
- mem.writeIntLittle(u32, code[4..8], aarch64.Instruction.b(displacement).toU32());
- // Just a placeholder populated in `populateLazyBindOffsetsInStubHelper`.
- mem.writeIntLittle(u32, code[8..12], 0x0);
- },
- else => unreachable,
- }
- try self.base.file.?.pwriteAll(code, stub_off);
-}
-
fn relocateSymbolTable(self: *MachO) !void {
const symtab = &self.load_commands.items[self.symtab_cmd_index.?].Symtab;
const nlocals = self.locals.items.len;