authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2021-07-20 20:33:07+02:00
committergravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2021-07-20 20:33:07+02:00
loga442b165f1e219b429e497e1de26780612762871
treea29e87e96c8280185e7cffbaa7424e1eb2a78b1e
parent1843ecf51b240c43a4a9a9cadbcc1286b9b9f41a

macho: add stub relocs when adding extern fn

in self-hosted.

4 files changed, 105 insertions(+), 114 deletions(-)

src/codegen.zig+22-29
......@@ -2523,36 +2523,29 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
25232523 }
25242524 } else if (func_value.castTag(.extern_fn)) |func_payload| {
25252525 const decl = func_payload.data;
2526 const decl_name = try std.fmt.allocPrint(self.bin_file.allocator, "_{s}", .{decl.name});
2527 defer self.bin_file.allocator.free(decl_name);
2528 const already_defined = macho_file.symbol_resolver.contains(decl_name);
2529 const resolv = macho_file.symbol_resolver.get(decl_name) orelse blk: {
2530 break :blk try macho_file.addExternFn(decl_name);
2531 };
2532 const start = self.code.items.len;
2533 const len: usize = blk: {
2534 switch (arch) {
2535 .x86_64 => {
2536 // callq
2537 try self.code.ensureCapacity(self.code.items.len + 5);
2538 self.code.appendSliceAssumeCapacity(&[5]u8{ 0xe8, 0x0, 0x0, 0x0, 0x0 });
2539 break :blk 5;
2540 },
2541 .aarch64 => {
2542 // bl
2543 writeInt(u32, try self.code.addManyAsArray(4), 0);
2544 break :blk 4;
2545 },
2546 else => unreachable, // unsupported architecture on MachO
2547 }
2548 };
2549 try macho_file.stub_fixups.append(self.bin_file.allocator, .{
2550 .symbol = resolv.where_index,
2551 .already_defined = already_defined,
2552 .start = start,
2553 .len = len,
2526 const where_index = try macho_file.addExternFn(mem.spanZ(decl.name));
2527 const offset = @intCast(u32, self.code.items.len);
2528 switch (arch) {
2529 .x86_64 => {
2530 // callq
2531 try self.code.ensureCapacity(self.code.items.len + 5);
2532 self.code.appendSliceAssumeCapacity(&[5]u8{ 0xe8, 0x0, 0x0, 0x0, 0x0 });
2533 },
2534 .aarch64 => {
2535 // bl
2536 writeInt(u32, try self.code.addManyAsArray(4), Instruction.bl(0).toU32());
2537 },
2538 else => unreachable, // unsupported architecture on MachO
2539 }
2540 // Add relocation to the decl.
2541 try decl.link.macho.relocs.append(self.bin_file.allocator, .{
2542 .offset = offset,
2543 .where = .import,
2544 .where_index = where_index,
2545 .payload = .{ .branch = .{
2546 .arch = arch,
2547 } },
25542548 });
2555 // We mark the space and fix it up later.
25562549 } else {
25572550 return self.fail(inst.base.src, "TODO implement calling bitcasted functions", .{});
25582551 }
src/link/MachO.zig+81-81
......@@ -159,6 +159,14 @@ strtab_needs_relocation: bool = false,
159159has_dices: bool = false,
160160has_stabs: bool = false,
161161
162pending_updates: std.ArrayListUnmanaged(struct {
163 kind: enum {
164 got,
165 stub,
166 },
167 index: u32,
168}) = .{},
169
162170/// A list of text blocks that have surplus capacity. This list can have false
163171/// positives, as functions grow and shrink over time, only sometimes being added
164172/// or removed from the freelist.
......@@ -179,6 +187,7 @@ text_block_free_list: std.ArrayListUnmanaged(*TextBlock) = .{},
179187/// Pointer to the last allocated text block
180188last_text_block: ?*TextBlock = null,
181189
190managed_blocks: std.ArrayListUnmanaged(TextBlock) = .{},
182191blocks: std.AutoHashMapUnmanaged(MatchingSection, *TextBlock) = .{},
183192
184193/// A list of all PIE fixups required for this run of the linker.
......@@ -190,13 +199,6 @@ blocks: std.AutoHashMapUnmanaged(MatchingSection, *TextBlock) = .{},
190199/// backends.
191200pie_fixups: std.ArrayListUnmanaged(PIEFixup) = .{},
192201
193/// A list of all stub (extern decls) fixups required for this run of the linker.
194/// Warning, this is currently NOT thread-safe. See the TODO below.
195/// TODO Move this list inside `updateDecl` where it should be allocated
196/// prior to calling `generateSymbol`, and then immediately deallocated
197/// rather than sitting in the global scope.
198stub_fixups: std.ArrayListUnmanaged(StubFixup) = .{},
199
200202const SymbolWithLoc = struct {
201203 // Table where the symbol can be found.
202204 where: enum {
......@@ -229,19 +231,6 @@ pub const PIEFixup = struct {
229231 size: usize,
230232};
231233
232pub const StubFixup = struct {
233 /// Id of extern (lazy) symbol.
234 symbol: u32,
235 /// Signals whether the symbol has already been declared before. If so,
236 /// then there is no need to rewrite the stub entry and related.
237 already_defined: bool,
238 /// Where in the byte stream we should perform the fixup.
239 start: usize,
240 /// The length of the byte stream. For x86_64, this will be
241 /// variable. For aarch64, it will be fixed at 4 bytes.
242 len: usize,
243};
244
245234/// When allocating, the ideal_capacity is calculated by
246235/// actual_capacity + (actual_capacity / ideal_factor)
247236const ideal_factor = 2;
......@@ -2244,9 +2233,7 @@ fn resolveSymbols(self: *MachO) !void {
22442233 .local_sym_index = local_sym_index,
22452234 };
22462235
2247 const block = try self.base.allocator.create(TextBlock);
2248 errdefer self.base.allocator.destroy(block);
2249
2236 const block = try self.managed_blocks.addOne(self.base.allocator);
22502237 block.* = TextBlock.empty;
22512238 block.local_sym_index = local_sym_index;
22522239 block.code = code;
......@@ -2382,9 +2369,7 @@ fn resolveSymbols(self: *MachO) !void {
23822369 // We create an empty atom for this symbol.
23832370 // TODO perhaps we should special-case special symbols? Create a separate
23842371 // linked list of atoms?
2385 const block = try self.base.allocator.create(TextBlock);
2386 errdefer self.base.allocator.destroy(block);
2387
2372 const block = try self.managed_blocks.addOne(self.base.allocator);
23882373 block.* = TextBlock.empty;
23892374 block.local_sym_index = local_sym_index;
23902375 block.code = try self.base.allocator.alloc(u8, 0);
......@@ -3243,9 +3228,8 @@ pub fn deinit(self: *MachO) void {
32433228 ds.deinit(self.base.allocator);
32443229 }
32453230
3231 self.pending_updates.deinit(self.base.allocator);
32463232 self.pie_fixups.deinit(self.base.allocator);
3247 self.stub_fixups.deinit(self.base.allocator);
3248 self.text_block_free_list.deinit(self.base.allocator);
32493233 self.got_entries.deinit(self.base.allocator);
32503234 self.got_entries_map.deinit(self.base.allocator);
32513235 self.got_entries_free_list.deinit(self.base.allocator);
......@@ -3288,8 +3272,12 @@ pub fn deinit(self: *MachO) void {
32883272 }
32893273 self.load_commands.deinit(self.base.allocator);
32903274
3291 // TODO dealloc all blocks
3275 for (self.managed_blocks.items) |*block| {
3276 block.deinit(self.base.allocator);
3277 }
3278 self.managed_blocks.deinit(self.base.allocator);
32923279 self.blocks.deinit(self.base.allocator);
3280 self.text_block_free_list.deinit(self.base.allocator);
32933281}
32943282
32953283pub fn closeFiles(self: MachO) void {
......@@ -3302,6 +3290,9 @@ pub fn closeFiles(self: MachO) void {
33023290}
33033291
33043292fn freeTextBlock(self: *MachO, text_block: *TextBlock) void {
3293 log.debug("freeTextBlock {*}", .{text_block});
3294 // text_block.deinit(self.base.allocator);
3295
33053296 var already_have_free_list_node = false;
33063297 {
33073298 var i: usize = 0;
......@@ -3467,18 +3458,22 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void {
34673458 .val = decl.val,
34683459 }, &code_buffer, .none);
34693460
3470 const code = switch (res) {
3471 .externally_managed => |x| x,
3472 .appended => code_buffer.items,
3473 .fail => |em| {
3474 // Clear any PIE fixups for this decl.
3475 self.pie_fixups.shrinkRetainingCapacity(0);
3476 // Clear any stub fixups for this decl.
3477 self.stub_fixups.shrinkRetainingCapacity(0);
3478 decl.analysis = .codegen_failure;
3479 try module.failed_decls.put(module.gpa, decl, em);
3480 return;
3481 },
3461 const code = blk: {
3462 switch (res) {
3463 .externally_managed => |x| break :blk x,
3464 .appended => {
3465 decl.link.macho.code = code_buffer.toOwnedSlice();
3466 log.warn("WAT", .{});
3467 break :blk decl.link.macho.code;
3468 },
3469 .fail => |em| {
3470 // Clear any PIE fixups for this decl.
3471 self.pie_fixups.shrinkRetainingCapacity(0);
3472 decl.analysis = .codegen_failure;
3473 try module.failed_decls.put(module.gpa, decl, em);
3474 return;
3475 },
3476 }
34823477 };
34833478
34843479 const required_alignment = decl.ty.abiAlignment(self.base.options.target);
......@@ -3559,12 +3554,12 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void {
35593554 switch (self.base.options.target.cpu.arch) {
35603555 .x86_64 => {
35613556 const displacement = try math.cast(u32, target_addr - this_addr - 4);
3562 mem.writeIntLittle(u32, code_buffer.items[fixup.offset..][0..4], displacement);
3557 mem.writeIntLittle(u32, decl.link.macho.code[fixup.offset..][0..4], displacement);
35633558 },
35643559 .aarch64 => {
35653560 // TODO optimize instruction based on jump length (use ldr(literal) + nop if possible).
35663561 {
3567 const inst = code_buffer.items[fixup.offset..][0..4];
3562 const inst = decl.link.macho.code[fixup.offset..][0..4];
35683563 var parsed = mem.bytesAsValue(meta.TagPayload(
35693564 aarch64.Instruction,
35703565 aarch64.Instruction.pc_relative_address,
......@@ -3576,7 +3571,7 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void {
35763571 parsed.immlo = @truncate(u2, pages);
35773572 }
35783573 {
3579 const inst = code_buffer.items[fixup.offset + 4 ..][0..4];
3574 const inst = decl.link.macho.code[fixup.offset + 4 ..][0..4];
35803575 var parsed = mem.bytesAsValue(meta.TagPayload(
35813576 aarch64.Instruction,
35823577 aarch64.Instruction.load_store_register,
......@@ -3590,39 +3585,24 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void {
35903585 }
35913586 }
35923587
3593 // Resolve stubs (if any)
3594 const text_segment = self.load_commands.items[self.text_segment_cmd_index.?].Segment;
3595 const stubs = text_segment.sections.items[self.stubs_section_index.?];
3596 for (self.stub_fixups.items) |fixup| {
3597 const stubs_index = self.stubs_map.get(fixup.symbol) orelse unreachable;
3598 const stub_addr = stubs.addr + stubs_index * stubs.reserved2;
3599 const text_addr = symbol.n_value + fixup.start;
3600 switch (self.base.options.target.cpu.arch) {
3601 .x86_64 => {
3602 assert(stub_addr >= text_addr + fixup.len);
3603 const displacement = try math.cast(u32, stub_addr - text_addr - fixup.len);
3604 var placeholder = code_buffer.items[fixup.start + fixup.len - @sizeOf(u32) ..][0..@sizeOf(u32)];
3605 mem.writeIntSliceLittle(u32, placeholder, displacement);
3606 },
3607 .aarch64 => {
3608 assert(stub_addr >= text_addr);
3609 const displacement = try math.cast(i28, stub_addr - text_addr);
3610 var placeholder = code_buffer.items[fixup.start..][0..fixup.len];
3611 mem.writeIntSliceLittle(u32, placeholder, aarch64.Instruction.bl(displacement).toU32());
3612 },
3613 else => unreachable, // unsupported target architecture
3614 }
3615 if (!fixup.already_defined) {
3616 try self.writeStub(stubs_index);
3617 try self.writeStubInStubHelper(stubs_index);
3618 try self.writeLazySymbolPointer(stubs_index);
3588 // Resolve relocations
3589 try decl.link.macho.resolveRelocs(self);
36193590
3620 self.rebase_info_dirty = true;
3621 self.lazy_binding_info_dirty = true;
3591 // Apply pending updates
3592 while (self.pending_updates.popOrNull()) |update| {
3593 switch (update.kind) {
3594 .got => unreachable,
3595 .stub => {
3596 try self.writeStub(update.index);
3597 try self.writeStubInStubHelper(update.index);
3598 try self.writeLazySymbolPointer(update.index);
3599 self.rebase_info_dirty = true;
3600 self.lazy_binding_info_dirty = true;
3601 },
36223602 }
36233603 }
3624 self.stub_fixups.shrinkRetainingCapacity(0);
36253604
3605 const text_segment = self.load_commands.items[self.text_segment_cmd_index.?].Segment;
36263606 const text_section = text_segment.sections.items[self.text_section_index.?];
36273607 const section_offset = symbol.n_value - text_section.addr;
36283608 const file_offset = text_section.offset + section_offset;
......@@ -3756,6 +3736,7 @@ pub fn deleteExport(self: *MachO, exp: Export) void {
37563736}
37573737
37583738pub fn freeDecl(self: *MachO, decl: *Module.Decl) void {
3739 log.debug("freeDecl {*}", .{decl});
37593740 // Appending to free lists is allowed to fail because the free lists are heuristics based anyway.
37603741 self.freeTextBlock(&decl.link.macho);
37613742 if (decl.link.macho.local_sym_index != 0) {
......@@ -4314,7 +4295,8 @@ fn allocateTextBlock(self: *MachO, text_block: *TextBlock, new_block_size: u64,
43144295 // should be deleted because the block that it points to has grown to take up
43154296 // more of the extra capacity.
43164297 if (!big_block.freeListEligible(self.*)) {
4317 _ = self.text_block_free_list.swapRemove(i);
4298 const bl = self.text_block_free_list.swapRemove(i);
4299 bl.deinit(self.base.allocator);
43184300 } else {
43194301 i += 1;
43204302 }
......@@ -4386,25 +4368,43 @@ fn allocateTextBlock(self: *MachO, text_block: *TextBlock, new_block_size: u64,
43864368 return vaddr;
43874369}
43884370
4389pub fn addExternFn(self: *MachO, name: []const u8) !SymbolWithLoc {
4390 log.debug("adding new extern function '{s}' with dylib ordinal 1", .{name});
4371pub fn addExternFn(self: *MachO, name: []const u8) !u32 {
4372 const sym_name = try std.fmt.allocPrint(self.base.allocator, "_{s}", .{name});
4373 const already_defined = self.symbol_resolver.contains(sym_name);
4374
4375 if (already_defined) {
4376 const resolv = self.symbol_resolver.get(sym_name) orelse unreachable;
4377 self.base.allocator.free(sym_name);
4378 return resolv.where_index;
4379 }
4380
4381 log.debug("adding new extern function '{s}' with dylib ordinal 1", .{sym_name});
43914382 const import_sym_index = @intCast(u32, self.imports.items.len);
43924383 try self.imports.append(self.base.allocator, .{
4393 .n_strx = try self.makeString(name),
4384 .n_strx = try self.makeString(sym_name),
43944385 .n_type = macho.N_UNDF | macho.N_EXT,
43954386 .n_sect = 0,
43964387 .n_desc = packDylibOrdinal(1),
43974388 .n_value = 0,
43984389 });
4399 const resolv = .{
4390 try self.symbol_resolver.putNoClobber(self.base.allocator, sym_name, .{
44004391 .where = .import,
44014392 .where_index = import_sym_index,
4402 };
4403 try self.symbol_resolver.putNoClobber(self.base.allocator, try self.base.allocator.dupe(u8, name), resolv);
4393 });
4394
44044395 const stubs_index = @intCast(u32, self.stubs.items.len);
44054396 try self.stubs.append(self.base.allocator, import_sym_index);
44064397 try self.stubs_map.putNoClobber(self.base.allocator, import_sym_index, stubs_index);
4407 return resolv;
4398
4399 // TODO discuss this. The caller context expects codegen.InnerError{ OutOfMemory, CodegenFail },
4400 // which obviously doesn't include file writing op errors. So instead of trying to write the stub
4401 // entry right here and now, queue it up and dispose of when updating decl.
4402 try self.pending_updates.append(self.base.allocator, .{
4403 .kind = .stub,
4404 .index = stubs_index,
4405 });
4406
4407 return import_sym_index;
44084408}
44094409
44104410const NextSegmentAddressAndOffset = struct {
src/link/MachO/Object.zig+1-3
......@@ -723,9 +723,7 @@ pub fn parseTextBlocks(self: *Object, macho_file: *MachO) !void {
723723 break :blk block_local_sym_index;
724724 };
725725
726 const block = try self.allocator.create(TextBlock);
727 errdefer self.allocator.destroy(block);
728
726 const block = try macho_file.managed_blocks.addOne(macho_file.base.allocator);
729727 block.* = TextBlock.empty;
730728 block.local_sym_index = block_local_sym_index;
731729 block.code = try self.allocator.dupe(u8, code);
src/link/MachO/TextBlock.zig+1-1
......@@ -558,9 +558,9 @@ pub fn deinit(self: *TextBlock, allocator: *Allocator) void {
558558 self.bindings.deinit(allocator);
559559 self.rebases.deinit(allocator);
560560 self.relocs.deinit(allocator);
561 self.allocator.free(self.code);
562561 self.contained.deinit(allocator);
563562 self.aliases.deinit(allocator);
563 allocator.free(self.code);
564564}
565565
566566/// Returns how much room there is to grow in virtual address space.