authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2021-08-21 22:26:02+02:00
committergravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2021-08-21 22:26:02+02:00
logea499203fe22dd3cd40753c020794790f32b91be
treec55005e655ddf885926757c43a07118cb2b4c4cd
parentd61d85abd2b998fea438e7317568537f927fa6a1

macho: incrementally write dyld_private and stub_helper atoms

By incrementally I mean using the incremental linker machinery and concepts. Currently, lots of repetition but already highlighted a potential problem with resolving relocations for symbols that weren't seen yet but wanting to write the atom to file (before seeing the relevant atoms).

1 files changed, 56 insertions(+), 138 deletions(-)

src/link/MachO.zig+56-138
...@@ -764,8 +764,14 @@ pub fn flush(self: *MachO, comp: *Compilation) !void {...@@ -764,8 +764,14 @@ pub fn flush(self: *MachO, comp: *Compilation) !void {
764 try self.createStubHelperPreambleAtom();764 try self.createStubHelperPreambleAtom();
765765
766 if (!use_stage1) {766 if (!use_stage1) {
767 // TODO this should be made common when I figure out how to prealloc space for traditional linker path.767 // TODO just a temp
768 try self.writeStubHelperPreamble();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 };
769 }775 }
770776
771 // Apply pending updates777 // Apply pending updates
...@@ -842,7 +848,6 @@ pub fn flushModule(self: *MachO, comp: *Compilation) !void {...@@ -842,7 +848,6 @@ pub fn flushModule(self: *MachO, comp: *Compilation) !void {
842 defer tracy.end();848 defer tracy.end();
843849
844 try self.setEntryPoint();850 try self.setEntryPoint();
845 try self.writeTextBlocks();
846 try self.writeRebaseInfoTable();851 try self.writeRebaseInfoTable();
847 try self.writeBindInfoTable();852 try self.writeBindInfoTable();
848 try self.writeLazyBindInfoTable();853 try self.writeLazyBindInfoTable();
...@@ -1989,7 +1994,29 @@ fn createDyldPrivateAtom(self: *MachO) !void {...@@ -1989,7 +1994,29 @@ fn createDyldPrivateAtom(self: *MachO) !void {
1989 .n_desc = 0,1994 .n_desc = 0,
1990 .n_value = 0,1995 .n_value = 0,
1991 });1996 });
1992 _ = try self.createEmptyAtom(match, local_sym_index, @sizeOf(u64), 3);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 }
19932020
1994 self.dyld_private_sym_index = local_sym_index;2021 self.dyld_private_sym_index = local_sym_index;
1995}2022}
...@@ -2019,6 +2046,7 @@ fn createStubHelperPreambleAtom(self: *MachO) !void {...@@ -2019,6 +2046,7 @@ fn createStubHelperPreambleAtom(self: *MachO) !void {
2019 .n_desc = 0,2046 .n_desc = 0,
2020 .n_value = 0,2047 .n_value = 0,
2021 });2048 });
2049 const last = self.blocks.get(match);
2022 const atom = try self.createEmptyAtom(match, local_sym_index, size, alignment);2050 const atom = try self.createEmptyAtom(match, local_sym_index, size, alignment);
2023 switch (arch) {2051 switch (arch) {
2024 .x86_64 => {2052 .x86_64 => {
...@@ -2130,6 +2158,29 @@ fn createStubHelperPreambleAtom(self: *MachO) !void {...@@ -2130,6 +2158,29 @@ fn createStubHelperPreambleAtom(self: *MachO) !void {
2130 }2158 }
2131 self.stub_preamble_sym_index = local_sym_index;2159 self.stub_preamble_sym_index = local_sym_index;
21322160
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);
2177
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);
2182 }
2183
2133 // TODO this needs to be fixed2184 // TODO this needs to be fixed
2134 // We already prealloc stub helper size in populateMissingMetadata(), but2185 // We already prealloc stub helper size in populateMissingMetadata(), but
2135 // perhaps it's not needed after all?2186 // perhaps it's not needed after all?
...@@ -2138,139 +2189,6 @@ fn createStubHelperPreambleAtom(self: *MachO) !void {...@@ -2138,139 +2189,6 @@ fn createStubHelperPreambleAtom(self: *MachO) !void {
2138 sect.size -= size;2189 sect.size -= size;
2139}2190}
21402191
2141fn writeStubHelperPreamble(self: *MachO) !void {
2142 const text_segment = &self.load_commands.items[self.text_segment_cmd_index.?].Segment;
2143 const stub_helper = &text_segment.sections.items[self.stub_helper_section_index.?];
2144 const data_const_segment = &self.load_commands.items[self.data_const_segment_cmd_index.?].Segment;
2145 const got = &data_const_segment.sections.items[self.got_section_index.?];
2146 const data_segment = &self.load_commands.items[self.data_segment_cmd_index.?].Segment;
2147 const data = &data_segment.sections.items[self.data_section_index.?];
2148
2149 self.stub_helper_stubs_start_off = blk: {
2150 switch (self.base.options.target.cpu.arch) {
2151 .x86_64 => {
2152 const code_size = 15;
2153 var code: [code_size]u8 = undefined;
2154 // lea %r11, [rip + disp]
2155 code[0] = 0x4c;
2156 code[1] = 0x8d;
2157 code[2] = 0x1d;
2158 {
2159 const target_addr = data.addr + data.size - @sizeOf(u64);
2160 const displacement = try math.cast(u32, target_addr - stub_helper.addr - 7);
2161 mem.writeIntLittle(u32, code[3..7], displacement);
2162 }
2163 // push %r11
2164 code[7] = 0x41;
2165 code[8] = 0x53;
2166 // jmp [rip + disp]
2167 code[9] = 0xff;
2168 code[10] = 0x25;
2169 {
2170 const got_index = self.got_entries_map.get(.{
2171 .where = .undef,
2172 .where_index = self.dyld_stub_binder_index.?,
2173 }) orelse unreachable;
2174 const addr = got.addr + got_index * @sizeOf(u64);
2175 const displacement = try math.cast(u32, addr - stub_helper.addr - code_size);
2176 mem.writeIntLittle(u32, code[11..], displacement);
2177 }
2178 try self.base.file.?.pwriteAll(&code, stub_helper.offset);
2179 break :blk stub_helper.offset + code_size;
2180 },
2181 .aarch64 => {
2182 var code: [6 * @sizeOf(u32)]u8 = undefined;
2183 data_blk_outer: {
2184 const this_addr = stub_helper.addr;
2185 const target_addr = data.addr + data.size - @sizeOf(u64);
2186 data_blk: {
2187 const displacement = math.cast(i21, target_addr - this_addr) catch break :data_blk;
2188 // adr x17, disp
2189 mem.writeIntLittle(u32, code[0..4], aarch64.Instruction.adr(.x17, displacement).toU32());
2190 // nop
2191 mem.writeIntLittle(u32, code[4..8], aarch64.Instruction.nop().toU32());
2192 break :data_blk_outer;
2193 }
2194 data_blk: {
2195 const new_this_addr = this_addr + @sizeOf(u32);
2196 const displacement = math.cast(i21, target_addr - new_this_addr) catch break :data_blk;
2197 // nop
2198 mem.writeIntLittle(u32, code[0..4], aarch64.Instruction.nop().toU32());
2199 // adr x17, disp
2200 mem.writeIntLittle(u32, code[4..8], aarch64.Instruction.adr(.x17, displacement).toU32());
2201 break :data_blk_outer;
2202 }
2203 // Jump is too big, replace adr with adrp and add.
2204 const this_page = @intCast(i32, this_addr >> 12);
2205 const target_page = @intCast(i32, target_addr >> 12);
2206 const pages = @intCast(i21, target_page - this_page);
2207 mem.writeIntLittle(u32, code[0..4], aarch64.Instruction.adrp(.x17, pages).toU32());
2208 const narrowed = @truncate(u12, target_addr);
2209 mem.writeIntLittle(u32, code[4..8], aarch64.Instruction.add(.x17, .x17, narrowed, false).toU32());
2210 }
2211 // stp x16, x17, [sp, #-16]!
2212 code[8] = 0xf0;
2213 code[9] = 0x47;
2214 code[10] = 0xbf;
2215 code[11] = 0xa9;
2216 binder_blk_outer: {
2217 const got_index = self.got_entries_map.get(.{
2218 .where = .undef,
2219 .where_index = self.dyld_stub_binder_index.?,
2220 }) orelse unreachable;
2221 const this_addr = stub_helper.addr + 3 * @sizeOf(u32);
2222 const target_addr = got.addr + got_index * @sizeOf(u64);
2223 binder_blk: {
2224 const displacement = math.divExact(u64, target_addr - this_addr, 4) catch break :binder_blk;
2225 const literal = math.cast(u18, displacement) catch break :binder_blk;
2226 // ldr x16, label
2227 mem.writeIntLittle(u32, code[12..16], aarch64.Instruction.ldr(.x16, .{
2228 .literal = literal,
2229 }).toU32());
2230 // nop
2231 mem.writeIntLittle(u32, code[16..20], aarch64.Instruction.nop().toU32());
2232 break :binder_blk_outer;
2233 }
2234 binder_blk: {
2235 const new_this_addr = this_addr + @sizeOf(u32);
2236 const displacement = math.divExact(u64, target_addr - new_this_addr, 4) catch break :binder_blk;
2237 const literal = math.cast(u18, displacement) catch break :binder_blk;
2238 // Pad with nop to please division.
2239 // nop
2240 mem.writeIntLittle(u32, code[12..16], aarch64.Instruction.nop().toU32());
2241 // ldr x16, label
2242 mem.writeIntLittle(u32, code[16..20], aarch64.Instruction.ldr(.x16, .{
2243 .literal = literal,
2244 }).toU32());
2245 break :binder_blk_outer;
2246 }
2247 // Use adrp followed by ldr(immediate).
2248 const this_page = @intCast(i32, this_addr >> 12);
2249 const target_page = @intCast(i32, target_addr >> 12);
2250 const pages = @intCast(i21, target_page - this_page);
2251 mem.writeIntLittle(u32, code[12..16], aarch64.Instruction.adrp(.x16, pages).toU32());
2252 const narrowed = @truncate(u12, target_addr);
2253 const offset = try math.divExact(u12, narrowed, 8);
2254 mem.writeIntLittle(u32, code[16..20], aarch64.Instruction.ldr(.x16, .{
2255 .register = .{
2256 .rn = .x16,
2257 .offset = aarch64.Instruction.LoadStoreOffset.imm(offset),
2258 },
2259 }).toU32());
2260 }
2261 // br x16
2262 code[20] = 0x00;
2263 code[21] = 0x02;
2264 code[22] = 0x1f;
2265 code[23] = 0xd6;
2266 try self.base.file.?.pwriteAll(&code, stub_helper.offset);
2267 break :blk stub_helper.offset + 6 * @sizeOf(u32);
2268 },
2269 else => unreachable,
2270 }
2271 };
2272}
2273
2274fn resolveSymbolsInObject(2192fn resolveSymbolsInObject(
2275 self: *MachO,2193 self: *MachO,
2276 object_id: u16,2194 object_id: u16,
...@@ -4873,7 +4791,7 @@ fn writeAllGlobalAndUndefSymbols(self: *MachO) !void {...@@ -4873,7 +4791,7 @@ fn writeAllGlobalAndUndefSymbols(self: *MachO) !void {
48734791
4874 const undefs_off = globals_off + globals_size;4792 const undefs_off = globals_off + globals_size;
4875 const undefs_size = nundefs * @sizeOf(macho.nlist_64);4793 const undefs_size = nundefs * @sizeOf(macho.nlist_64);
4876 log.debug("writing extern symbols from 0x{x} to 0x{x}", .{ undefs_off, undefs_size + undefs_off });4794 log.debug("writing undef symbols from 0x{x} to 0x{x}", .{ undefs_off, undefs_size + undefs_off });
4877 try self.base.file.?.pwriteAll(mem.sliceAsBytes(self.undefs.items), undefs_off);4795 try self.base.file.?.pwriteAll(mem.sliceAsBytes(self.undefs.items), undefs_off);
48784796
4879 // Update dynamic symbol table.4797 // Update dynamic symbol table.