authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2021-08-23 22:12:04+02:00
committergravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2021-08-23 22:12:04+02:00
log799c5bb9551dafd76f9d1fce7d3f5a01ac55da83
tree2951228b30769bddbacee40faba6cf4230ba54ef
parentea499203fe22dd3cd40753c020794790f32b91be

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.

1 files changed, 173 insertions(+), 174 deletions(-)

src/link/MachO.zig+173-174
......@@ -177,8 +177,6 @@ has_stabs: bool = false,
177177
178178section_ordinals: std.AutoArrayHashMapUnmanaged(MatchingSection, void) = .{},
179179
180pending_updates: std.ArrayListUnmanaged(PendingUpdate) = .{},
181
182180/// A list of text blocks that have surplus capacity. This list can have false
183181/// positives, as functions grow and shrink over time, only sometimes being added
184182/// or removed from the freelist.
......@@ -384,6 +382,26 @@ pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Optio
384382 try ds.writeLocalSymbol(0);
385383 }
386384
385 {
386 const atom = try self.createDyldPrivateAtom();
387 const match = MatchingSection{
388 .seg = self.data_segment_cmd_index.?,
389 .sect = self.data_section_index.?,
390 };
391 const vaddr = try self.allocateAtom(atom, match);
392 try self.writeAtom(atom, match);
393 }
394
395 {
396 const atom = try self.createStubHelperPreambleAtom();
397 const match = MatchingSection{
398 .seg = self.text_segment_cmd_index.?,
399 .sect = self.stub_helper_section_index.?,
400 };
401 const vaddr = try self.allocateAtom(atom, match);
402 try self.writeAtom(atom, match);
403 }
404
387405 return self;
388406}
389407
......@@ -760,50 +778,42 @@ pub fn flush(self: *MachO, comp: *Compilation) !void {
760778 try self.parseLibs(libs.items, self.base.options.sysroot);
761779 try self.resolveSymbols();
762780 try self.resolveDyldStubBinder();
763 try self.createDyldPrivateAtom();
764 try self.createStubHelperPreambleAtom();
765
766 if (!use_stage1) {
767 // TODO just a temp
768 const seg = self.load_commands.items[self.text_segment_cmd_index.?].Segment;
769 const sect = seg.sections.items[self.stub_helper_section_index.?];
770 self.stub_helper_stubs_start_off = sect.offset + switch (self.base.options.target.cpu.arch) {
771 .x86_64 => @intCast(u64, 15),
772 .aarch64 => @intCast(u64, 6 * @sizeOf(u32)),
773 else => unreachable,
774 };
775 }
776
777 // Apply pending updates
778 var still_pending = std.ArrayList(PendingUpdate).init(self.base.allocator);
779 defer still_pending.deinit();
780
781 for (self.pending_updates.items) |update| {
782 switch (update) {
783 .add_stub_entry => |stub_index| {
784 try self.writeStub(stub_index);
785 try self.writeStubInStubHelper(stub_index);
786 try self.writeLazySymbolPointer(stub_index);
787 self.rebase_info_dirty = true;
788 self.lazy_binding_info_dirty = true;
789 },
790 else => unreachable,
791 }
792 }
793
794 self.pending_updates.clearRetainingCapacity();
795 for (still_pending.items) |update| {
796 self.pending_updates.appendAssumeCapacity(update);
797 }
798
799 try self.parseTextBlocks();
800781 try self.addRpathLCs(rpath_table.keys());
801782 try self.addLoadDylibLCs();
802783 try self.addDataInCodeLC();
803784 try self.addCodeSignatureLC();
804785
805786 if (use_stage1) {
787 try self.parseTextBlocks();
806788 try self.sortSections();
789 {
790 const atom = try self.createDyldPrivateAtom();
791 try self.allocateAtomStage1(atom, .{
792 .seg = self.data_segment_cmd_index.?,
793 .sect = self.data_section_index.?,
794 });
795 }
796 {
797 const atom = try self.createStubHelperPreambleAtom();
798 try self.allocateAtomStage1(atom, .{
799 .seg = self.text_segment_cmd_index.?,
800 .sect = self.stub_helper_section_index.?,
801 });
802
803 // TODO this is just a temp
804 // We already prealloc stub helper size in populateMissingMetadata(), but
805 // perhaps it's not needed after all?
806 const seg = &self.load_commands.items[self.text_segment_cmd_index.?].Segment;
807 const sect = &seg.sections.items[self.stub_helper_section_index.?];
808 sect.size -= atom.size;
809 }
810 for (self.stubs.items) |_| {
811 const atom = try self.createStubHelperAtom();
812 try self.allocateAtomStage1(atom, .{
813 .seg = self.text_segment_cmd_index.?,
814 .sect = self.stub_helper_section_index.?,
815 });
816 }
807817 try self.allocateTextSegment();
808818 try self.allocateDataConstSegment();
809819 try self.allocateDataSegment();
......@@ -1705,18 +1715,10 @@ fn allocateTextSegment(self: *MachO) !void {
17051715 seg.inner.fileoff = 0;
17061716 seg.inner.vmaddr = base_vmaddr;
17071717
1708 // Set stubs and stub_helper sizes
1718 // Set stubs sizes
17091719 const stubs = &seg.sections.items[self.stubs_section_index.?];
1710 const stub_helper = &seg.sections.items[self.stub_helper_section_index.?];
17111720 stubs.size += nstubs * stubs.reserved2;
17121721
1713 const stub_size: u4 = switch (self.base.options.target.cpu.arch) {
1714 .x86_64 => 10,
1715 .aarch64 => 3 * @sizeOf(u32),
1716 else => unreachable,
1717 };
1718 stub_helper.size += nstubs * stub_size;
1719
17201722 var sizeofcmds: u64 = 0;
17211723 for (self.load_commands.items) |lc| {
17221724 sizeofcmds += lc.cmdsize();
......@@ -1947,41 +1949,81 @@ fn createEmptyAtom(
19471949 defer self.base.allocator.free(code);
19481950 mem.set(u8, code, 0);
19491951
1950 const block = try self.base.allocator.create(TextBlock);
1951 errdefer self.base.allocator.destroy(block);
1952 block.* = TextBlock.empty;
1953 block.local_sym_index = local_sym_index;
1954 block.size = size;
1955 block.alignment = alignment;
1956 try block.code.appendSlice(self.base.allocator, code);
1952 const atom = try self.base.allocator.create(TextBlock);
1953 errdefer self.base.allocator.destroy(atom);
1954 atom.* = TextBlock.empty;
1955 atom.local_sym_index = local_sym_index;
1956 atom.size = size;
1957 atom.alignment = alignment;
1958 try atom.code.appendSlice(self.base.allocator, code);
1959 try self.managed_blocks.append(self.base.allocator, atom);
19571960
1958 try self.managed_blocks.append(self.base.allocator, block);
1961 return atom;
1962}
1963
1964fn allocateAtom(self: *MachO, atom: *TextBlock, match: MatchingSection) !u64 {
1965 // TODO converge with `allocateTextBlock`
1966 const seg = self.load_commands.items[match.seg].Segment;
1967 const sect = seg.sections.items[match.sect];
1968 const base_addr = if (atom.prev) |prev| blk: {
1969 const prev_atom_sym = self.locals.items[prev.local_sym_index];
1970 break :blk prev_atom_sym.n_value;
1971 } else sect.addr;
1972 const atom_alignment = try math.powi(u32, 2, atom.alignment);
1973 const vaddr = mem.alignForwardGeneric(u64, base_addr, atom_alignment);
1974
1975 // TODO we should check if we need to expand the section or not like we
1976 // do in `allocateTextBlock`.
1977 if (self.blocks.getPtr(match)) |last| {
1978 last.*.next = atom;
1979 atom.prev = last.*;
1980 last.* = atom;
1981 } else {
1982 try self.blocks.putNoClobber(self.base.allocator, match, atom);
1983 }
19591984
1985 return vaddr;
1986}
1987
1988fn writeAtom(self: *MachO, atom: *TextBlock, match: MatchingSection) !void {
1989 const seg = self.load_commands.items[match.seg].Segment;
1990 const sect = seg.sections.items[match.sect];
1991
1992 const vaddr = try self.allocateAtom(atom, match);
1993 const sym = &self.locals.items[atom.local_sym_index];
1994 sym.n_value = vaddr;
1995 sym.n_sect = @intCast(u8, self.section_ordinals.getIndex(match).? + 1);
1996
1997 try atom.resolveRelocs(self);
1998
1999 const file_offset = sect.offset + vaddr - sect.addr;
2000 log.debug("writing atom for symbol {s} at file offset 0x{x}", .{ self.getString(sym.n_strx), file_offset });
2001 try self.base.file.?.pwriteAll(atom.code.items, file_offset);
2002 try self.writeLocalSymbol(atom.local_sym_index);
2003}
2004
2005fn allocateAtomStage1(self: *MachO, atom: *TextBlock, match: MatchingSection) !void {
19602006 // Update target section's metadata
19612007 // TODO should we update segment's size here too?
19622008 // How does it tie with incremental space allocs?
19632009 const tseg = &self.load_commands.items[match.seg].Segment;
19642010 const tsect = &tseg.sections.items[match.sect];
1965 const new_alignment = math.max(tsect.@"align", alignment);
2011 const new_alignment = math.max(tsect.@"align", atom.alignment);
19662012 const new_alignment_pow_2 = try math.powi(u32, 2, new_alignment);
1967 const new_size = mem.alignForwardGeneric(u64, tsect.size, new_alignment_pow_2) + size;
2013 const new_size = mem.alignForwardGeneric(u64, tsect.size, new_alignment_pow_2) + atom.size;
19682014 tsect.size = new_size;
19692015 tsect.@"align" = new_alignment;
19702016
19712017 if (self.blocks.getPtr(match)) |last| {
1972 last.*.next = block;
1973 block.prev = last.*;
1974 last.* = block;
2018 last.*.next = atom;
2019 atom.prev = last.*;
2020 last.* = atom;
19752021 } else {
1976 try self.blocks.putNoClobber(self.base.allocator, match, block);
2022 try self.blocks.putNoClobber(self.base.allocator, match, atom);
19772023 }
1978
1979 return block;
19802024}
19812025
1982fn createDyldPrivateAtom(self: *MachO) !void {
1983 if (self.dyld_private_sym_index != null) return;
1984
2026fn createDyldPrivateAtom(self: *MachO) !*TextBlock {
19852027 const match = MatchingSection{
19862028 .seg = self.data_segment_cmd_index.?,
19872029 .sect = self.data_section_index.?,
......@@ -1994,35 +2036,11 @@ fn createDyldPrivateAtom(self: *MachO) !void {
19942036 .n_desc = 0,
19952037 .n_value = 0,
19962038 });
1997 const last = self.blocks.get(match);
1998 const atom = try self.createEmptyAtom(match, local_sym_index, @sizeOf(u64), 3);
1999
2000 if (!(build_options.is_stage1 and self.base.options.use_stage1)) {
2001 const seg = self.load_commands.items[match.seg].Segment;
2002 const sect = seg.sections.items[match.sect];
2003 const base_addr = if (last) |last_atom| blk: {
2004 const last_atom_sym = self.locals.items[last_atom.local_sym_index];
2005 break :blk last_atom_sym.n_value;
2006 } else sect.addr;
2007 const n_sect = @intCast(u8, self.section_ordinals.getIndex(match).? + 1);
2008 const atom_alignment = try math.powi(u32, 2, atom.alignment);
2009 const vaddr = mem.alignForwardGeneric(u64, base_addr, atom_alignment);
2010
2011 const sym = &self.locals.items[local_sym_index];
2012 sym.n_value = vaddr;
2013 sym.n_sect = n_sect;
2014
2015 const file_offset = sect.offset + vaddr - sect.addr;
2016 log.debug("writing code for symbol {s} at file offset 0x{x}", .{ self.getString(sym.n_strx), file_offset });
2017 try self.base.file.?.pwriteAll(atom.code.items, file_offset);
2018 try self.writeLocalSymbol(local_sym_index);
2019 }
2020
20212039 self.dyld_private_sym_index = local_sym_index;
2040 return self.createEmptyAtom(match, local_sym_index, @sizeOf(u64), 3);
20222041}
20232042
2024fn createStubHelperPreambleAtom(self: *MachO) !void {
2025 if (self.stub_preamble_sym_index != null) return;
2043fn createStubHelperPreambleAtom(self: *MachO) !*TextBlock {
20262044 const arch = self.base.options.target.cpu.arch;
20272045 const match = MatchingSection{
20282046 .seg = self.text_segment_cmd_index.?,
......@@ -2046,7 +2064,6 @@ fn createStubHelperPreambleAtom(self: *MachO) !void {
20462064 .n_desc = 0,
20472065 .n_value = 0,
20482066 });
2049 const last = self.blocks.get(match);
20502067 const atom = try self.createEmptyAtom(match, local_sym_index, size, alignment);
20512068 switch (arch) {
20522069 .x86_64 => {
......@@ -2157,36 +2174,71 @@ fn createStubHelperPreambleAtom(self: *MachO) !void {
21572174 else => unreachable,
21582175 }
21592176 self.stub_preamble_sym_index = local_sym_index;
2177 return atom;
2178}
21602179
2161 if (!(build_options.is_stage1 and self.base.options.use_stage1)) {
2162 const seg = self.load_commands.items[match.seg].Segment;
2163 const sect = seg.sections.items[match.sect];
2164 const base_addr = if (last) |last_atom| blk: {
2165 const last_atom_sym = self.locals.items[last_atom.local_sym_index];
2166 break :blk last_atom_sym.n_value;
2167 } else sect.addr;
2168 const n_sect = @intCast(u8, self.section_ordinals.getIndex(match).? + 1);
2169 const atom_alignment = try math.powi(u32, 2, atom.alignment);
2170 const vaddr = mem.alignForwardGeneric(u64, base_addr, atom_alignment);
2171
2172 const sym = &self.locals.items[local_sym_index];
2173 sym.n_value = vaddr;
2174 sym.n_sect = n_sect;
2175
2176 try atom.resolveRelocs(self);
2180fn createStubHelperAtom(self: *MachO) !*TextBlock {
2181 const arch = self.base.options.target.cpu.arch;
2182 const stub_size: u4 = switch (arch) {
2183 .x86_64 => 10,
2184 .aarch64 => 3 * @sizeOf(u32),
2185 else => unreachable,
2186 };
2187 const local_sym_index = @intCast(u32, self.locals.items.len);
2188 try self.locals.append(self.base.allocator, .{
2189 .n_strx = try self.makeString("stub_in_stub_helper"),
2190 .n_type = macho.N_SECT,
2191 .n_sect = 0,
2192 .n_desc = 0,
2193 .n_value = 0,
2194 });
2195 const atom = try self.createEmptyAtom(.{
2196 .seg = self.text_segment_cmd_index.?,
2197 .sect = self.stub_helper_section_index.?,
2198 }, local_sym_index, stub_size, 2);
2199 try atom.relocs.ensureTotalCapacity(self.base.allocator, 1);
21772200
2178 const file_offset = sect.offset + vaddr - sect.addr;
2179 log.debug("writing code for symbol {s} at file offset 0x{x}", .{ self.getString(sym.n_strx), file_offset });
2180 try self.base.file.?.pwriteAll(atom.code.items, file_offset);
2181 try self.writeLocalSymbol(local_sym_index);
2201 switch (arch) {
2202 .x86_64 => {
2203 // pushq
2204 atom.code.items[0] = 0x68;
2205 // Next 4 bytes 1..4 are just a placeholder populated in `populateLazyBindOffsetsInStubHelper`.
2206 // jmpq
2207 atom.code.items[5] = 0xe9;
2208 atom.relocs.appendAssumeCapacity(.{
2209 .offset = 6,
2210 .where = .local,
2211 .where_index = self.stub_preamble_sym_index.?,
2212 .payload = .{
2213 .branch = .{ .arch = arch },
2214 },
2215 });
2216 },
2217 .aarch64 => {
2218 const literal = blk: {
2219 const div_res = try math.divExact(u64, stub_size - @sizeOf(u32), 4);
2220 break :blk try math.cast(u18, div_res);
2221 };
2222 // ldr w16, literal
2223 mem.writeIntLittle(u32, atom.code.items[0..4], aarch64.Instruction.ldr(.w16, .{
2224 .literal = literal,
2225 }).toU32());
2226 // b disp
2227 mem.writeIntLittle(u32, atom.code.items[4..8], aarch64.Instruction.b(0).toU32());
2228 atom.relocs.appendAssumeCapacity(.{
2229 .offset = 4,
2230 .where = .local,
2231 .where_index = self.stub_preamble_sym_index.?,
2232 .payload = .{
2233 .branch = .{ .arch = arch },
2234 },
2235 });
2236 // Next 4 bytes 8..12 are just a placeholder populated in `populateLazyBindOffsetsInStubHelper`.
2237 },
2238 else => unreachable,
21822239 }
21832240
2184 // TODO this needs to be fixed
2185 // We already prealloc stub helper size in populateMissingMetadata(), but
2186 // perhaps it's not needed after all?
2187 const seg = &self.load_commands.items[self.text_segment_cmd_index.?].Segment;
2188 const sect = &seg.sections.items[self.stub_helper_section_index.?];
2189 sect.size -= size;
2241 return atom;
21902242}
21912243
21922244fn resolveSymbolsInObject(
......@@ -2681,7 +2733,6 @@ fn flushZld(self: *MachO) !void {
26812733 // TODO weak bound pointers
26822734 try self.writeLazySymbolPointer(index);
26832735 try self.writeStub(index);
2684 try self.writeStubInStubHelper(index);
26852736 }
26862737
26872738 if (self.common_section_index) |index| {
......@@ -3173,7 +3224,6 @@ pub fn deinit(self: *MachO) void {
31733224 }
31743225
31753226 self.section_ordinals.deinit(self.base.allocator);
3176 self.pending_updates.deinit(self.base.allocator);
31773227 self.got_entries.deinit(self.base.allocator);
31783228 self.got_entries_map.deinit(self.base.allocator);
31793229 self.got_entries_free_list.deinit(self.base.allocator);
......@@ -4404,11 +4454,7 @@ pub fn addExternFn(self: *MachO, name: []const u8) !u32 {
44044454 try self.stubs.append(self.base.allocator, sym_index);
44054455 try self.stubs_map.putNoClobber(self.base.allocator, sym_index, stubs_index);
44064456
4407 // TODO discuss this. The caller context expects codegen.InnerError{ OutOfMemory, CodegenFail },
4408 // which obviously doesn't include file writing op errors. So instead of trying to write the stub
4409 // entry right here and now, queue it up and dispose of when updating decl.
4410 try self.pending_updates.ensureUnusedCapacity(self.base.allocator, 1);
4411 self.pending_updates.appendAssumeCapacity(.{ .add_stub_entry = stubs_index });
4457 // TODO create and write stub, stub_helper and lazy_ptr atoms
44124458
44134459 return sym_index;
44144460}
......@@ -4683,53 +4729,6 @@ fn writeStub(self: *MachO, index: u32) !void {
46834729 try self.base.file.?.pwriteAll(code, stub_off);
46844730}
46854731
4686fn writeStubInStubHelper(self: *MachO, index: u32) !void {
4687 const text_segment = self.load_commands.items[self.text_segment_cmd_index.?].Segment;
4688 const stub_helper = text_segment.sections.items[self.stub_helper_section_index.?];
4689
4690 const stub_size: u4 = switch (self.base.options.target.cpu.arch) {
4691 .x86_64 => 10,
4692 .aarch64 => 3 * @sizeOf(u32),
4693 else => unreachable,
4694 };
4695 const stub_off = self.stub_helper_stubs_start_off.? + index * stub_size;
4696
4697 var code = try self.base.allocator.alloc(u8, stub_size);
4698 defer self.base.allocator.free(code);
4699
4700 switch (self.base.options.target.cpu.arch) {
4701 .x86_64 => {
4702 const displacement = try math.cast(
4703 i32,
4704 @intCast(i64, stub_helper.offset) - @intCast(i64, stub_off) - stub_size,
4705 );
4706 // pushq
4707 code[0] = 0x68;
4708 mem.writeIntLittle(u32, code[1..][0..4], 0x0); // Just a placeholder populated in `populateLazyBindOffsetsInStubHelper`.
4709 // jmpq
4710 code[5] = 0xe9;
4711 mem.writeIntLittle(u32, code[6..][0..4], @bitCast(u32, displacement));
4712 },
4713 .aarch64 => {
4714 const literal = blk: {
4715 const div_res = try math.divExact(u64, stub_size - @sizeOf(u32), 4);
4716 break :blk try math.cast(u18, div_res);
4717 };
4718 // ldr w16, literal
4719 mem.writeIntLittle(u32, code[0..4], aarch64.Instruction.ldr(.w16, .{
4720 .literal = literal,
4721 }).toU32());
4722 const displacement = try math.cast(i28, @intCast(i64, stub_helper.offset) - @intCast(i64, stub_off) - 4);
4723 // b disp
4724 mem.writeIntLittle(u32, code[4..8], aarch64.Instruction.b(displacement).toU32());
4725 // Just a placeholder populated in `populateLazyBindOffsetsInStubHelper`.
4726 mem.writeIntLittle(u32, code[8..12], 0x0);
4727 },
4728 else => unreachable,
4729 }
4730 try self.base.file.?.pwriteAll(code, stub_off);
4731}
4732
47334732fn relocateSymbolTable(self: *MachO) !void {
47344733 const symtab = &self.load_commands.items[self.symtab_cmd_index.?].Symtab;
47354734 const nlocals = self.locals.items.len;