From f44732c1b0d7516c4a8169f7381cbcf55e1ae460 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Wed, 6 Jan 2021 01:13:25 +0100 Subject: macho: populate stubs and stub_helper --- src/codegen.zig | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) (limited to 'src/codegen.zig') diff --git a/src/codegen.zig b/src/codegen.zig index d83eba2219..7100227db0 100644 --- a/src/codegen.zig +++ b/src/codegen.zig @@ -1859,8 +1859,26 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { }, else => unreachable, // unsupported architecture on MachO } - } else if (func_value.castTag(.extern_fn)) |_| { - return self.fail(inst.base.src, "TODO implement calling extern functions", .{}); + } else if (func_value.castTag(.extern_fn)) |func_payload| { + const decl = func_payload.data; + const decl_name = try std.fmt.allocPrint(self.bin_file.allocator, "_{s}", .{decl.name}); + defer self.bin_file.allocator.free(decl_name); + const name = try macho_file.makeString(decl_name); + const symbol = macho_file.undef_symbols.items.len; + try macho_file.undef_symbols.append(self.bin_file.allocator, .{ + .n_strx = name, + .n_type = std.macho.N_UNDF | std.macho.N_EXT, + .n_sect = 0, + .n_desc = std.macho.REFERENCE_FLAG_UNDEFINED_NON_LAZY | std.macho.N_SYMBOL_RESOLVER, + .n_value = 0, + }); + try macho_file.stub_fixups.append(self.bin_file.allocator, .{ + .symbol = symbol, + .start = self.code.items.len, + .len = 4, + }); + // We mark the space and fix it up later. + writeInt(u32, try self.code.addManyAsArray(4), 0); } else { return self.fail(inst.base.src, "TODO implement calling bitcasted functions", .{}); } -- cgit v1.2.3 From 44a052a65f5c59b02383178f36fcc231df28b49f Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Fri, 8 Jan 2021 22:26:18 +0100 Subject: macho: write out stubs for new externs only --- src/codegen.zig | 30 ++++++++++++------ src/link/MachO.zig | 93 ++++++++++++++++++++++++++++-------------------------- 2 files changed, 68 insertions(+), 55 deletions(-) (limited to 'src/codegen.zig') diff --git a/src/codegen.zig b/src/codegen.zig index 7100227db0..84148fcdd4 100644 --- a/src/codegen.zig +++ b/src/codegen.zig @@ -1862,18 +1862,28 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { } else if (func_value.castTag(.extern_fn)) |func_payload| { const decl = func_payload.data; const decl_name = try std.fmt.allocPrint(self.bin_file.allocator, "_{s}", .{decl.name}); - defer self.bin_file.allocator.free(decl_name); - const name = try macho_file.makeString(decl_name); - const symbol = macho_file.undef_symbols.items.len; - try macho_file.undef_symbols.append(self.bin_file.allocator, .{ - .n_strx = name, - .n_type = std.macho.N_UNDF | std.macho.N_EXT, - .n_sect = 0, - .n_desc = std.macho.REFERENCE_FLAG_UNDEFINED_NON_LAZY | std.macho.N_SYMBOL_RESOLVER, - .n_value = 0, - }); + const exists: bool = macho_file.externs.contains(decl_name); + const symbol: u32 = blk: { + if (macho_file.externs.get(decl_name)) |index| { + self.bin_file.allocator.free(decl_name); + break :blk index; + } else { + const extern_index = @intCast(u32, macho_file.undef_symbols.items.len - 1); // TODO + try macho_file.externs.putNoClobber(self.bin_file.allocator, decl_name, extern_index); + const name = try macho_file.makeString(decl_name); + try macho_file.undef_symbols.append(self.bin_file.allocator, .{ + .n_strx = name, + .n_type = std.macho.N_UNDF | std.macho.N_EXT, + .n_sect = 0, + .n_desc = std.macho.REFERENCE_FLAG_UNDEFINED_NON_LAZY | std.macho.N_SYMBOL_RESOLVER, + .n_value = 0, + }); + break :blk extern_index; + } + }; try macho_file.stub_fixups.append(self.bin_file.allocator, .{ .symbol = symbol, + .exists = exists, .start = self.code.items.len, .len = 4, }); diff --git a/src/link/MachO.zig b/src/link/MachO.zig index e83fab6294..3c876bf59d 100644 --- a/src/link/MachO.zig +++ b/src/link/MachO.zig @@ -167,9 +167,11 @@ last_text_block: ?*TextBlock = null, pie_fixups: std.ArrayListUnmanaged(PieFixup) = .{}, stub_fixups: std.ArrayListUnmanaged(StubFixup) = .{}, +externs: std.StringHashMapUnmanaged(u32) = .{}, pub const StubFixup = struct { - symbol: usize, + symbol: u32, + exists: bool, start: usize, len: usize, }; @@ -1263,56 +1265,57 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void { const stub_h = &text_segment.sections.items[self.stub_helper_section_index.?]; const data_segment = &self.load_commands.items[self.data_segment_cmd_index.?].Segment; const la_ptr = &data_segment.sections.items[self.la_symbol_ptr_section_index.?]; - for (self.stub_fixups.items) |fixup, idx| { - const i = @intCast(u32, idx); + for (self.stub_fixups.items) |fixup| { // TODO increment offset for stub writing - const stub_addr = stubs.addr + i * stubs.reserved2; + const stub_addr = stubs.addr + fixup.symbol * stubs.reserved2; const text_addr = symbol.n_value + fixup.start; const displacement = @intCast(u32, stub_addr - text_addr); var placeholder = code_buffer.items[fixup.start..][0..fixup.len]; mem.writeIntSliceLittle(u32, placeholder, aarch64.Instruction.bl(@intCast(i28, displacement)).toU32()); - const end = stub_h.addr + self.next_stub_helper_off.? - stub_h.offset; - var buf: [@sizeOf(u64)]u8 = undefined; - mem.writeIntLittle(u64, &buf, end); - try self.base.file.?.pwriteAll(&buf, la_ptr.offset + i * @sizeOf(u64)); - - const la_ptr_addr = la_ptr.addr + i * @sizeOf(u64); - const displacement2 = la_ptr_addr - stub_addr; - var ccode: [2 * @sizeOf(u32)]u8 = undefined; - mem.writeIntLittle(u32, ccode[0..4], aarch64.Instruction.ldr(.x16, .{ - .literal = @intCast(u19, displacement2 / 4), - }).toU32()); - mem.writeIntLittle(u32, ccode[4..8], aarch64.Instruction.br(.x16).toU32()); - try self.base.file.?.pwriteAll(&ccode, stubs.offset + i * stubs.reserved2); - - const displacement3 = @intCast(i64, stub_h.addr) - @intCast(i64, end + 4); - var cccode: [3 * @sizeOf(u32)]u8 = undefined; - mem.writeIntLittle(u32, cccode[0..4], aarch64.Instruction.ldr(.w16, .{ - .literal = 0x2, - }).toU32()); - mem.writeIntLittle(u32, cccode[4..8], aarch64.Instruction.b(@intCast(i28, displacement3)).toU32()); - mem.writeIntLittle(u32, cccode[8..12], i * 0xd); - try self.base.file.?.pwriteAll(&cccode, self.next_stub_helper_off.?); - self.next_stub_helper_off = self.next_stub_helper_off.? + 3 * @sizeOf(u32); - - try self.rebase_info_table.symbols.append(self.base.allocator, .{ - .segment = 3, - .offset = i * stubs.reserved2, - }); - self.rebase_info_dirty = true; - - const sym = self.undef_symbols.items[fixup.symbol]; - const name_str = self.getString(sym.n_strx); - var name = try self.base.allocator.alloc(u8, name_str.len); - mem.copy(u8, name, name_str); - try self.lazy_binding_info_table.symbols.append(self.base.allocator, .{ - .segment = 3, - .offset = i * @sizeOf(u64), - .dylib_ordinal = 1, - .name = name, - }); - self.lazy_binding_info_dirty = true; + if (!fixup.exists) { + const end = stub_h.addr + self.next_stub_helper_off.? - stub_h.offset; + var buf: [@sizeOf(u64)]u8 = undefined; + mem.writeIntLittle(u64, &buf, end); + try self.base.file.?.pwriteAll(&buf, la_ptr.offset + fixup.symbol * @sizeOf(u64)); + + const la_ptr_addr = la_ptr.addr + fixup.symbol * @sizeOf(u64); + const displacement2 = la_ptr_addr - stub_addr; + var ccode: [2 * @sizeOf(u32)]u8 = undefined; + mem.writeIntLittle(u32, ccode[0..4], aarch64.Instruction.ldr(.x16, .{ + .literal = @intCast(u19, displacement2 / 4), + }).toU32()); + mem.writeIntLittle(u32, ccode[4..8], aarch64.Instruction.br(.x16).toU32()); + try self.base.file.?.pwriteAll(&ccode, stubs.offset + fixup.symbol * stubs.reserved2); + + const displacement3 = @intCast(i64, stub_h.addr) - @intCast(i64, end + 4); + var cccode: [3 * @sizeOf(u32)]u8 = undefined; + mem.writeIntLittle(u32, cccode[0..4], aarch64.Instruction.ldr(.w16, .{ + .literal = 0x2, + }).toU32()); + mem.writeIntLittle(u32, cccode[4..8], aarch64.Instruction.b(@intCast(i28, displacement3)).toU32()); + mem.writeIntLittle(u32, cccode[8..12], fixup.symbol * 0xd); + try self.base.file.?.pwriteAll(&cccode, self.next_stub_helper_off.?); + self.next_stub_helper_off = self.next_stub_helper_off.? + 3 * @sizeOf(u32); + + try self.rebase_info_table.symbols.append(self.base.allocator, .{ + .segment = 3, + .offset = fixup.symbol * stubs.reserved2, + }); + self.rebase_info_dirty = true; + + const sym = self.undef_symbols.items[fixup.symbol + 1]; + const name_str = self.getString(sym.n_strx); + var name = try self.base.allocator.alloc(u8, name_str.len); + mem.copy(u8, name, name_str); + try self.lazy_binding_info_table.symbols.append(self.base.allocator, .{ + .segment = 3, + .offset = fixup.symbol * @sizeOf(u64), + .dylib_ordinal = 1, + .name = name, + }); + self.lazy_binding_info_dirty = true; + } } self.stub_fixups.shrinkRetainingCapacity(0); -- cgit v1.2.3 From b86d0e488b9dfc7d943da86a34998360e24da225 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Sat, 9 Jan 2021 19:29:33 +0100 Subject: macho: refactor writing and managing externs --- src/codegen.zig | 26 ++- src/link/MachO.zig | 383 ++++++++++++++++++++++----------------- src/link/MachO/imports.zig | 440 +++++++++++++-------------------------------- 3 files changed, 359 insertions(+), 490 deletions(-) (limited to 'src/codegen.zig') diff --git a/src/codegen.zig b/src/codegen.zig index 84148fcdd4..ad4215191f 100644 --- a/src/codegen.zig +++ b/src/codegen.zig @@ -1861,29 +1861,27 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { } } else if (func_value.castTag(.extern_fn)) |func_payload| { const decl = func_payload.data; + // We don't free the decl_name immediately unless it already exists. + // If it doesn't, it will get autofreed when we clean up the extern symbol table. const decl_name = try std.fmt.allocPrint(self.bin_file.allocator, "_{s}", .{decl.name}); - const exists: bool = macho_file.externs.contains(decl_name); + const already_defined = macho_file.extern_lazy_symbols.contains(decl_name); const symbol: u32 = blk: { - if (macho_file.externs.get(decl_name)) |index| { + if (macho_file.extern_lazy_symbols.get(decl_name)) |sym| { self.bin_file.allocator.free(decl_name); - break :blk index; + break :blk sym.index; } else { - const extern_index = @intCast(u32, macho_file.undef_symbols.items.len - 1); // TODO - try macho_file.externs.putNoClobber(self.bin_file.allocator, decl_name, extern_index); - const name = try macho_file.makeString(decl_name); - try macho_file.undef_symbols.append(self.bin_file.allocator, .{ - .n_strx = name, - .n_type = std.macho.N_UNDF | std.macho.N_EXT, - .n_sect = 0, - .n_desc = std.macho.REFERENCE_FLAG_UNDEFINED_NON_LAZY | std.macho.N_SYMBOL_RESOLVER, - .n_value = 0, + const index = @intCast(u32, macho_file.extern_lazy_symbols.items().len); + try macho_file.extern_lazy_symbols.putNoClobber(self.bin_file.allocator, decl_name, .{ + .name = decl_name, + .dylib_ordinal = 1, // TODO this is now hardcoded, since we only support libSystem. + .index = index, }); - break :blk extern_index; + break :blk index; } }; try macho_file.stub_fixups.append(self.bin_file.allocator, .{ .symbol = symbol, - .exists = exists, + .already_defined = already_defined, .start = self.code.items.len, .len = 4, }); diff --git a/src/link/MachO.zig b/src/link/MachO.zig index 04941c4b68..7d86c27aa9 100644 --- a/src/link/MachO.zig +++ b/src/link/MachO.zig @@ -105,16 +105,17 @@ entry_addr: ?u64 = null, /// Table of all local symbols /// Internally references string table for names (which are optional). local_symbols: std.ArrayListUnmanaged(macho.nlist_64) = .{}, -/// Table of all defined global symbols +/// Table of all global symbols global_symbols: std.ArrayListUnmanaged(macho.nlist_64) = .{}, -/// Table of all undefined symbols -undef_symbols: std.ArrayListUnmanaged(macho.nlist_64) = .{}, +/// Table of all extern nonlazy symbols, indexed by name. +extern_nonlazy_symbols: std.StringArrayHashMapUnmanaged(ExternSymbol) = .{}, +/// Table of all extern lazy symbols, indexed by name. +extern_lazy_symbols: std.StringArrayHashMapUnmanaged(ExternSymbol) = .{}, local_symbol_free_list: std.ArrayListUnmanaged(u32) = .{}, global_symbol_free_list: std.ArrayListUnmanaged(u32) = .{}, offset_table_free_list: std.ArrayListUnmanaged(u32) = .{}, -dyld_stub_binder_index: ?u16 = null, stub_helper_stubs_start_off: ?u64 = null, /// Table of symbol names aka the string table. @@ -123,13 +124,6 @@ string_table: std.ArrayListUnmanaged(u8) = .{}, /// Table of trampolines to the actual symbols in __text section. offset_table: std.ArrayListUnmanaged(u64) = .{}, -/// Table of rebase info entries. -rebase_info_table: RebaseInfoTable = .{}, -/// Table of binding info entries. -binding_info_table: BindingInfoTable = .{}, -/// Table of lazy binding info entries. -lazy_binding_info_table: LazyBindingInfoTable = .{}, - error_flags: File.ErrorFlags = File.ErrorFlags{}, offset_table_count_dirty: bool = false, @@ -167,11 +161,10 @@ last_text_block: ?*TextBlock = null, pie_fixups: std.ArrayListUnmanaged(PieFixup) = .{}, stub_fixups: std.ArrayListUnmanaged(StubFixup) = .{}, -externs: std.StringHashMapUnmanaged(u32) = .{}, pub const StubFixup = struct { symbol: u32, - exists: bool, + already_defined: bool, start: usize, len: usize, }; @@ -920,42 +913,42 @@ fn linkWithLLD(self: *MachO, comp: *Compilation) !void { return error.NoSymbolTableFound; } - // Parse dyld info - try self.parseBindingInfoTable(); - try self.parseLazyBindingInfoTable(); + // // Parse dyld info + // try self.parseBindingInfoTable(); + // try self.parseLazyBindingInfoTable(); - // Update the dylib ordinals. - self.binding_info_table.dylib_ordinal = next_ordinal; - for (self.lazy_binding_info_table.symbols.items) |*symbol| { - symbol.dylib_ordinal = next_ordinal; - } + // // Update the dylib ordinals. + // self.binding_info_table.dylib_ordinal = next_ordinal; + // for (self.lazy_binding_info_table.symbols.items) |*symbol| { + // symbol.dylib_ordinal = next_ordinal; + // } - // Write updated dyld info. - const dyld_info = self.load_commands.items[self.dyld_info_cmd_index.?].DyldInfoOnly; - { - const size = try self.binding_info_table.calcSize(); - assert(dyld_info.bind_size >= size); + // // Write updated dyld info. + // const dyld_info = self.load_commands.items[self.dyld_info_cmd_index.?].DyldInfoOnly; + // { + // const size = try self.binding_info_table.calcSize(); + // assert(dyld_info.bind_size >= size); - var buffer = try self.base.allocator.alloc(u8, @intCast(usize, size)); - defer self.base.allocator.free(buffer); + // var buffer = try self.base.allocator.alloc(u8, @intCast(usize, size)); + // defer self.base.allocator.free(buffer); - var stream = std.io.fixedBufferStream(buffer); - try self.binding_info_table.write(stream.writer()); + // var stream = std.io.fixedBufferStream(buffer); + // try self.binding_info_table.write(stream.writer()); - try self.base.file.?.pwriteAll(buffer, dyld_info.bind_off); - } - { - const size = try self.lazy_binding_info_table.calcSize(); - assert(dyld_info.lazy_bind_size >= size); + // try self.base.file.?.pwriteAll(buffer, dyld_info.bind_off); + // } + // { + // const size = try self.lazy_binding_info_table.calcSize(); + // assert(dyld_info.lazy_bind_size >= size); - var buffer = try self.base.allocator.alloc(u8, @intCast(usize, size)); - defer self.base.allocator.free(buffer); + // var buffer = try self.base.allocator.alloc(u8, @intCast(usize, size)); + // defer self.base.allocator.free(buffer); - var stream = std.io.fixedBufferStream(buffer); - try self.lazy_binding_info_table.write(stream.writer()); + // var stream = std.io.fixedBufferStream(buffer); + // try self.lazy_binding_info_table.write(stream.writer()); - try self.base.file.?.pwriteAll(buffer, dyld_info.lazy_bind_off); - } + // try self.base.file.?.pwriteAll(buffer, dyld_info.lazy_bind_off); + // } // Write updated load commands and the header try self.writeLoadCommands(); @@ -1037,14 +1030,13 @@ pub fn deinit(self: *MachO) void { if (self.d_sym) |*ds| { ds.deinit(self.base.allocator); } - self.binding_info_table.deinit(self.base.allocator); - self.lazy_binding_info_table.deinit(self.base.allocator); self.pie_fixups.deinit(self.base.allocator); self.text_block_free_list.deinit(self.base.allocator); self.offset_table.deinit(self.base.allocator); self.offset_table_free_list.deinit(self.base.allocator); self.string_table.deinit(self.base.allocator); - self.undef_symbols.deinit(self.base.allocator); + self.extern_lazy_symbols.deinit(self.base.allocator); + self.extern_nonlazy_symbols.deinit(self.base.allocator); self.global_symbols.deinit(self.base.allocator); self.global_symbol_free_list.deinit(self.base.allocator); self.local_symbols.deinit(self.base.allocator); @@ -1261,59 +1253,28 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void { } // Resolve stubs (if any) - const stubs = &text_segment.sections.items[self.stubs_section_index.?]; - const stub_h = &text_segment.sections.items[self.stub_helper_section_index.?]; - const data_segment = &self.load_commands.items[self.data_segment_cmd_index.?].Segment; - const la_ptr = &data_segment.sections.items[self.la_symbol_ptr_section_index.?]; + const stubs = text_segment.sections.items[self.stubs_section_index.?]; for (self.stub_fixups.items) |fixup| { - // TODO increment offset for stub writing const stub_addr = stubs.addr + fixup.symbol * stubs.reserved2; const text_addr = symbol.n_value + fixup.start; const displacement = @intCast(u32, stub_addr - text_addr); var placeholder = code_buffer.items[fixup.start..][0..fixup.len]; - mem.writeIntSliceLittle(u32, placeholder, aarch64.Instruction.bl(@intCast(i28, displacement)).toU32()); - - if (!fixup.exists) { - const stub_off = self.stub_helper_stubs_start_off.? + fixup.symbol * 3 * @sizeOf(u32); - const end = stub_h.addr + stub_off - stub_h.offset; - var buf: [@sizeOf(u64)]u8 = undefined; - mem.writeIntLittle(u64, &buf, end); - try self.base.file.?.pwriteAll(&buf, la_ptr.offset + fixup.symbol * @sizeOf(u64)); - - const la_ptr_addr = la_ptr.addr + fixup.symbol * @sizeOf(u64); - const displacement2 = la_ptr_addr - stub_addr; - var ccode: [2 * @sizeOf(u32)]u8 = undefined; - mem.writeIntLittle(u32, ccode[0..4], aarch64.Instruction.ldr(.x16, .{ - .literal = @intCast(u19, displacement2 / 4), - }).toU32()); - mem.writeIntLittle(u32, ccode[4..8], aarch64.Instruction.br(.x16).toU32()); - try self.base.file.?.pwriteAll(&ccode, stubs.offset + fixup.symbol * stubs.reserved2); - - const displacement3 = @intCast(i64, stub_h.addr) - @intCast(i64, end + 4); - var cccode: [3 * @sizeOf(u32)]u8 = undefined; - mem.writeIntLittle(u32, cccode[0..4], aarch64.Instruction.ldr(.w16, .{ - .literal = 0x2, - }).toU32()); - mem.writeIntLittle(u32, cccode[4..8], aarch64.Instruction.b(@intCast(i28, displacement3)).toU32()); - mem.writeIntLittle(u32, cccode[8..12], fixup.symbol * 0xd); - try self.base.file.?.pwriteAll(&cccode, stub_off); - - try self.rebase_info_table.symbols.append(self.base.allocator, .{ - .segment = 3, - .offset = fixup.symbol * stubs.reserved2, - }); + switch (self.base.options.target.cpu.arch) { + .x86_64 => return error.TODOImplementStubFixupsForx86_64, + .aarch64 => { + mem.writeIntSliceLittle(u32, placeholder, aarch64.Instruction.bl(@intCast(i28, displacement)).toU32()); + }, + else => unreachable, + } + if (!fixup.already_defined) { + try self.writeStub(fixup.symbol); + try self.writeStubInStubHelper(fixup.symbol); + try self.writeLazySymbolPointer(fixup.symbol); + + const extern_sym = &self.extern_lazy_symbols.items()[fixup.symbol].value; + extern_sym.segment = self.data_segment_cmd_index.?; + extern_sym.offset = fixup.symbol * @sizeOf(u64); self.rebase_info_dirty = true; - - const sym = self.undef_symbols.items[fixup.symbol + 1]; - const name_str = self.getString(sym.n_strx); - var name = try self.base.allocator.alloc(u8, name_str.len); - mem.copy(u8, name, name_str); - try self.lazy_binding_info_table.symbols.append(self.base.allocator, .{ - .segment = 3, - .offset = fixup.symbol * @sizeOf(u64), - .dylib_ordinal = 1, - .name = name, - }); self.lazy_binding_info_dirty = true; } } @@ -2080,51 +2041,51 @@ pub fn populateMissingMetadata(self: *MachO) !void { self.header_dirty = true; self.load_commands_dirty = true; } - if (self.dyld_stub_binder_index == null) { - self.dyld_stub_binder_index = @intCast(u16, self.undef_symbols.items.len); - const name = try self.makeString("dyld_stub_binder"); - try self.undef_symbols.append(self.base.allocator, .{ - .n_strx = name, - .n_type = macho.N_UNDF | macho.N_EXT, - .n_sect = 0, - .n_desc = macho.REFERENCE_FLAG_UNDEFINED_NON_LAZY | macho.N_SYMBOL_RESOLVER, - .n_value = 0, - }); - - self.binding_info_table.dylib_ordinal = 1; - const nn = self.getString(name); - var n = try self.base.allocator.alloc(u8, nn.len); - mem.copy(u8, n, nn); - try self.binding_info_table.symbols.append(self.base.allocator, .{ - .name = n, - .segment = 2, - .offset = 0, + if (!self.extern_nonlazy_symbols.contains("dyld_stub_binder")) { + const index = @intCast(u32, self.extern_nonlazy_symbols.items().len); + const name = try std.fmt.allocPrint(self.base.allocator, "dyld_stub_binder", .{}); + try self.extern_nonlazy_symbols.putNoClobber(self.base.allocator, name, .{ + .name = name, + .dylib_ordinal = 1, // TODO this is currently hardcoded. + .index = index, + .segment = self.data_const_segment_cmd_index.?, + .offset = index * @sizeOf(u64), }); self.binding_info_dirty = true; } if (self.stub_helper_stubs_start_off == null) { - const text = &self.load_commands.items[self.text_segment_cmd_index.?].Segment; - const sh = &text.sections.items[self.stub_helper_section_index.?]; - const data = &self.load_commands.items[self.data_segment_cmd_index.?].Segment; - const data_data = &data.sections.items[self.data_section_index.?]; - const displacement = data_data.addr - sh.addr; - var code: [4 * @sizeOf(u32)]u8 = undefined; - mem.writeIntLittle(u32, code[0..4], aarch64.Instruction.adr(.x17, @intCast(i21, displacement)).toU32()); - mem.writeIntLittle(u32, code[4..8], aarch64.Instruction.stp( - .x16, - .x17, - aarch64.Register.sp, - aarch64.Instruction.LoadStorePairOffset.pre_index(-16), - ).toU32()); - const dc = &self.load_commands.items[self.data_const_segment_cmd_index.?].Segment; - const got = &dc.sections.items[self.data_got_section_index.?]; - const displacement2 = got.addr - sh.addr - 2 * @sizeOf(u32); - mem.writeIntLittle(u32, code[8..12], aarch64.Instruction.ldr(.x16, .{ - .literal = @intCast(u19, displacement2 / 4), - }).toU32()); - mem.writeIntLittle(u32, code[12..16], aarch64.Instruction.br(.x16).toU32()); - self.stub_helper_stubs_start_off = sh.offset + 4 * @sizeOf(u32); - try self.base.file.?.pwriteAll(&code, sh.offset); + 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 data_segment = &self.load_commands.items[self.data_segment_cmd_index.?].Segment; + const data = &data_segment.sections.items[self.data_section_index.?]; + const data_const_segment = &self.load_commands.items[self.data_const_segment_cmd_index.?].Segment; + const got = &data_const_segment.sections.items[self.data_got_section_index.?]; + switch (self.base.options.target.cpu.arch) { + .x86_64 => return error.TODOImplementStubHelperForX86_64, + .aarch64 => { + var code: [4 * @sizeOf(u32)]u8 = undefined; + { + const displacement = data.addr - stub_helper.addr; + mem.writeIntLittle(u32, code[0..4], aarch64.Instruction.adr(.x17, @intCast(i21, displacement)).toU32()); + } + mem.writeIntLittle(u32, code[4..8], aarch64.Instruction.stp( + .x16, + .x17, + aarch64.Register.sp, + aarch64.Instruction.LoadStorePairOffset.pre_index(-16), + ).toU32()); + { + const displacement = got.addr - stub_helper.addr - 2 * @sizeOf(u32); + mem.writeIntLittle(u32, code[8..12], aarch64.Instruction.ldr(.x16, .{ + .literal = @intCast(u19, displacement / 4), + }).toU32()); + } + mem.writeIntLittle(u32, code[12..16], aarch64.Instruction.br(.x16).toU32()); + self.stub_helper_stubs_start_off = stub_helper.offset + 4 * @sizeOf(u32); + try self.base.file.?.pwriteAll(&code, stub_helper.offset); + }, + else => unreachable, + } } } @@ -2460,11 +2421,73 @@ fn writeOffsetTableEntry(self: *MachO, index: usize) !void { try self.base.file.?.pwriteAll(&code, off); } +fn writeLazySymbolPointer(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 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 stub_off = self.stub_helper_stubs_start_off.? + index * 3 * @sizeOf(u32); + const end = stub_helper.addr + stub_off - stub_helper.offset; + var buf: [@sizeOf(u64)]u8 = undefined; + mem.writeIntLittle(u64, &buf, end); + const off = la_symbol_ptr.offset + index * @sizeOf(u64); + log.debug("writing lazy symbol pointer entry 0x{x} at 0x{x}", .{ end, off }); + try self.base.file.?.pwriteAll(&buf, off); +} + +fn writeStub(self: *MachO, index: u32) !void { + const text_segment = self.load_commands.items[self.text_segment_cmd_index.?].Segment; + const stubs = text_segment.sections.items[self.stubs_section_index.?]; + 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 stub_off = stubs.offset + index * stubs.reserved2; + const stub_addr = stubs.addr + index * stubs.reserved2; + const la_ptr_addr = la_symbol_ptr.addr + index * @sizeOf(u64); + const displacement = la_ptr_addr - stub_addr; + log.debug("writing stub at 0x{x}", .{stub_off}); + switch (self.base.options.target.cpu.arch) { + .x86_64 => return error.TODOImplementWritingStubsForx86_64, + .aarch64 => { + var code: [2 * @sizeOf(u32)]u8 = undefined; + mem.writeIntLittle(u32, code[0..4], aarch64.Instruction.ldr(.x16, .{ + .literal = @intCast(u19, displacement / 4), + }).toU32()); + mem.writeIntLittle(u32, code[4..8], aarch64.Instruction.br(.x16).toU32()); + try self.base.file.?.pwriteAll(&code, stub_off); + }, + else => unreachable, + } +} + +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_off = self.stub_helper_stubs_start_off.? + index * 3 * @sizeOf(u32); + const end = stub_helper.addr + stub_off - stub_helper.offset; + const displacement = @intCast(i64, stub_helper.addr) - @intCast(i64, end + 4); + switch (self.base.options.target.cpu.arch) { + .x86_64 => return error.TODOImplementWritingStubsInStubHelperForx86_64, + .aarch64 => { + var code: [3 * @sizeOf(u32)]u8 = undefined; + mem.writeIntLittle(u32, code[0..4], aarch64.Instruction.ldr(.w16, .{ + .literal = 0x2, + }).toU32()); + mem.writeIntLittle(u32, code[4..8], aarch64.Instruction.b(@intCast(i28, displacement)).toU32()); + mem.writeIntLittle(u32, code[8..12], index * 0xd); // TODO This is the size of lazy binding opcode block. + try self.base.file.?.pwriteAll(&code, stub_off); + }, + else => unreachable, + } +} + fn relocateSymbolTable(self: *MachO) !void { const symtab = &self.load_commands.items[self.symtab_cmd_index.?].Symtab; const nlocals = self.local_symbols.items.len; const nglobals = self.global_symbols.items.len; - const nundefs = self.undef_symbols.items.len; + const nundefs = self.extern_lazy_symbols.items().len + self.extern_nonlazy_symbols.items().len; const nsyms = nlocals + nglobals + nundefs; if (symtab.nsyms < nsyms) { @@ -2509,7 +2532,31 @@ fn writeAllGlobalAndUndefSymbols(self: *MachO) !void { const symtab = &self.load_commands.items[self.symtab_cmd_index.?].Symtab; const nlocals = self.local_symbols.items.len; const nglobals = self.global_symbols.items.len; - const nundefs = self.undef_symbols.items.len; + + const nundefs = self.extern_lazy_symbols.items().len + self.extern_nonlazy_symbols.items().len; + var undefs = std.ArrayList(macho.nlist_64).init(self.base.allocator); + defer undefs.deinit(); + try undefs.ensureCapacity(nundefs); + for (self.extern_lazy_symbols.items()) |entry| { + const name = try self.makeString(entry.key); + undefs.appendAssumeCapacity(.{ + .n_strx = name, + .n_type = std.macho.N_UNDF | std.macho.N_EXT, + .n_sect = 0, + .n_desc = std.macho.REFERENCE_FLAG_UNDEFINED_NON_LAZY | std.macho.N_SYMBOL_RESOLVER, + .n_value = 0, + }); + } + for (self.extern_nonlazy_symbols.items()) |entry| { + const name = try self.makeString(entry.key); + undefs.appendAssumeCapacity(.{ + .n_strx = name, + .n_type = std.macho.N_UNDF | std.macho.N_EXT, + .n_sect = 0, + .n_desc = std.macho.REFERENCE_FLAG_UNDEFINED_NON_LAZY | std.macho.N_SYMBOL_RESOLVER, + .n_value = 0, + }); + } const locals_off = symtab.symoff; const locals_size = nlocals * @sizeOf(macho.nlist_64); @@ -2521,8 +2568,8 @@ fn writeAllGlobalAndUndefSymbols(self: *MachO) !void { const undefs_off = globals_off + globals_size; const undefs_size = nundefs * @sizeOf(macho.nlist_64); - log.debug("writing undef symbols from 0x{x} to 0x{x}", .{ undefs_off, undefs_size + undefs_off }); - try self.base.file.?.pwriteAll(mem.sliceAsBytes(self.undef_symbols.items), undefs_off); + log.debug("writing extern symbols from 0x{x} to 0x{x}", .{ undefs_off, undefs_size + undefs_off }); + try self.base.file.?.pwriteAll(mem.sliceAsBytes(undefs.items), undefs_off); // Update dynamic symbol table. const dysymtab = &self.load_commands.items[self.dysymtab_cmd_index.?].Dysymtab; @@ -2546,42 +2593,33 @@ fn writeIndirectSymbolTable(self: *MachO) !void { var buf: [@sizeOf(u32)]u8 = undefined; var off = dysymtab.indirectsymoff; - var idx: u32 = 0; stubs.reserved1 = 0; - for (self.undef_symbols.items) |sym, i| { - if (i == self.dyld_stub_binder_index.?) { - continue; - } - const symtab_idx = @intCast(u32, dysymtab.iundefsym + i); + for (self.extern_lazy_symbols.items()) |entry| { + const symtab_idx = @intCast(u32, dysymtab.iundefsym + entry.value.index); mem.writeIntLittle(u32, &buf, symtab_idx); try self.base.file.?.pwriteAll(&buf, off); off += @sizeOf(u32); dysymtab.nindirectsyms += 1; - idx += 1; } - got.reserved1 = @intCast(u32, self.undef_symbols.items.len - 1); - if (self.dyld_stub_binder_index) |i| { - const symtab_idx = i + dysymtab.iundefsym; + const base_id = @intCast(u32, self.extern_lazy_symbols.items().len); + got.reserved1 = base_id; + for (self.extern_nonlazy_symbols.items()) |entry| { + const symtab_idx = @intCast(u32, dysymtab.iundefsym + entry.value.index + base_id); mem.writeIntLittle(u32, &buf, symtab_idx); try self.base.file.?.pwriteAll(&buf, off); off += @sizeOf(u32); dysymtab.nindirectsyms += 1; - idx += 1; } - la.reserved1 = got.reserved1 + 1; - for (self.undef_symbols.items) |sym, i| { - if (i == self.dyld_stub_binder_index.?) { - continue; - } - const symtab_idx = @intCast(u32, dysymtab.iundefsym + i); + la.reserved1 = got.reserved1 + @intCast(u32, self.extern_nonlazy_symbols.items().len); + for (self.extern_lazy_symbols.items()) |entry| { + const symtab_idx = @intCast(u32, dysymtab.iundefsym + entry.value.index); mem.writeIntLittle(u32, &buf, symtab_idx); try self.base.file.?.pwriteAll(&buf, off); off += @sizeOf(u32); dysymtab.nindirectsyms += 1; - idx += 1; } } @@ -2689,12 +2727,19 @@ fn writeRebaseInfoTable(self: *MachO) !void { const tracy = trace(@src()); defer tracy.end(); - const size = try self.rebase_info_table.calcSize(); + var symbols = try self.base.allocator.alloc(*const ExternSymbol, self.extern_lazy_symbols.items().len); + defer self.base.allocator.free(symbols); + + for (self.extern_lazy_symbols.items()) |*entry, i| { + symbols[i] = &entry.value; + } + + const size = try rebaseInfoSize(symbols); var buffer = try self.base.allocator.alloc(u8, @intCast(usize, size)); defer self.base.allocator.free(buffer); var stream = std.io.fixedBufferStream(buffer); - try self.rebase_info_table.write(stream.writer()); + try writeRebaseInfo(symbols, stream.writer()); const linkedit_segment = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment; const dyld_info = &self.load_commands.items[self.dyld_info_cmd_index.?].DyldInfoOnly; @@ -2720,12 +2765,19 @@ fn writeBindingInfoTable(self: *MachO) !void { const tracy = trace(@src()); defer tracy.end(); - const size = try self.binding_info_table.calcSize(); + var symbols = try self.base.allocator.alloc(*const ExternSymbol, self.extern_nonlazy_symbols.items().len); + defer self.base.allocator.free(symbols); + + for (self.extern_nonlazy_symbols.items()) |*entry, i| { + symbols[i] = &entry.value; + } + + const size = try bindInfoSize(symbols); var buffer = try self.base.allocator.alloc(u8, @intCast(usize, size)); defer self.base.allocator.free(buffer); var stream = std.io.fixedBufferStream(buffer); - try self.binding_info_table.write(stream.writer()); + try writeBindInfo(symbols, stream.writer()); const linkedit_segment = self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment; const dyld_info = &self.load_commands.items[self.dyld_info_cmd_index.?].DyldInfoOnly; @@ -2748,12 +2800,19 @@ fn writeBindingInfoTable(self: *MachO) !void { fn writeLazyBindingInfoTable(self: *MachO) !void { if (!self.lazy_binding_info_dirty) return; - const size = try self.lazy_binding_info_table.calcSize(); + var symbols = try self.base.allocator.alloc(*const ExternSymbol, self.extern_lazy_symbols.items().len); + defer self.base.allocator.free(symbols); + + for (self.extern_lazy_symbols.items()) |*entry, i| { + symbols[i] = &entry.value; + } + + const size = try lazyBindInfoSize(symbols); var buffer = try self.base.allocator.alloc(u8, @intCast(usize, size)); defer self.base.allocator.free(buffer); var stream = std.io.fixedBufferStream(buffer); - try self.lazy_binding_info_table.write(stream.writer()); + try writeLazyBindInfo(symbols, stream.writer()); const linkedit_segment = self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment; const dyld_info = &self.load_commands.items[self.dyld_info_cmd_index.?].DyldInfoOnly; @@ -3001,7 +3060,7 @@ fn parseBindingInfoTable(self: *MachO) !void { assert(nread == buffer.len); var stream = std.io.fixedBufferStream(buffer); - try self.binding_info_table.read(stream.reader(), self.base.allocator); + // try self.binding_info_table.read(stream.reader(), self.base.allocator); } fn parseLazyBindingInfoTable(self: *MachO) !void { @@ -3012,5 +3071,5 @@ fn parseLazyBindingInfoTable(self: *MachO) !void { assert(nread == buffer.len); var stream = std.io.fixedBufferStream(buffer); - try self.lazy_binding_info_table.read(stream.reader(), self.base.allocator); + // try self.lazy_binding_info_table.read(stream.reader(), self.base.allocator); } diff --git a/src/link/MachO/imports.zig b/src/link/MachO/imports.zig index 6128992af3..8e0f72e1de 100644 --- a/src/link/MachO/imports.zig +++ b/src/link/MachO/imports.zig @@ -6,365 +6,177 @@ const mem = std.mem; const assert = std.debug.assert; const Allocator = mem.Allocator; -pub const RebaseInfoTable = struct { - rebase_type: u8 = macho.REBASE_TYPE_POINTER, - symbols: std.ArrayListUnmanaged(Symbol) = .{}, +pub const ExternSymbol = struct { + /// Symbol name. + /// We own the memory, therefore we'll need to free it by calling `deinit`. + /// In self-hosted, we don't expect it to be null ever. + /// However, this is for backwards compatibility with LLD when + /// we'll be patching things up post mortem. + name: ?[]u8 = null, - pub const Symbol = struct { - segment: u8, - offset: i64, - }; - - pub fn deinit(self: *RebaseInfoTable, allocator: *Allocator) void { - self.symbols.deinit(allocator); - } - - /// Write the rebase info table to byte stream. - pub fn write(self: RebaseInfoTable, writer: anytype) !void { - for (self.symbols.items) |symbol| { - try writer.writeByte(macho.REBASE_OPCODE_SET_TYPE_IMM | @truncate(u4, self.rebase_type)); - try writer.writeByte(macho.REBASE_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB | @truncate(u4, symbol.segment)); - try leb.writeILEB128(writer, symbol.offset); - try writer.writeByte(macho.REBASE_OPCODE_DO_REBASE_IMM_TIMES | @truncate(u4, 1)); - } - - try writer.writeByte(macho.REBASE_OPCODE_DONE); - } + /// Id of the dynamic library where the specified entries can be found. + /// Id of 0 means self. + /// TODO this should really be an id into the table of all defined + /// dylibs. + dylib_ordinal: i64 = 0, - /// Calculate size in bytes of this rebase info table. - pub fn calcSize(self: *RebaseInfoTable) !u64 { - var stream = std.io.countingWriter(std.io.null_writer); - var writer = stream.writer(); - var size: u64 = 0; + segment: u16 = 0, + offset: u32 = 0, + addend: ?i32 = null, + index: u32, - for (self.symbols.items) |symbol| { - size += 2; - try leb.writeILEB128(writer, symbol.offset); - size += 1; + pub fn deinit(self: *ExternSymbol, allocator: *Allocator) void { + if (self.name) |*name| { + allocator.free(name); } - - size += 1 + stream.bytes_written; - return size; } }; -/// Table of binding info entries used to tell the dyld which -/// symbols to bind at loading time. -pub const BindingInfoTable = struct { - /// Id of the dynamic library where the specified entries can be found. - dylib_ordinal: i64 = 0, - - /// Binding type; defaults to pointer type. - binding_type: u8 = macho.BIND_TYPE_POINTER, - - symbols: std.ArrayListUnmanaged(Symbol) = .{}, +pub fn rebaseInfoSize(symbols: []*const ExternSymbol) !u64 { + var stream = std.io.countingWriter(std.io.null_writer); + var writer = stream.writer(); + var size: u64 = 0; - pub const Symbol = struct { - /// Symbol name. - name: ?[]u8 = null, + for (symbols) |symbol| { + size += 2; + try leb.writeILEB128(writer, symbol.offset); + size += 1; + } - /// Id of the segment where to bind this symbol to. - segment: u8, + size += 1 + stream.bytes_written; + return size; +} - /// Offset of this symbol wrt to the segment id encoded in `segment`. - offset: i64, +pub fn writeRebaseInfo(symbols: []*const ExternSymbol, writer: anytype) !void { + for (symbols) |symbol| { + try writer.writeByte(macho.REBASE_OPCODE_SET_TYPE_IMM | @truncate(u4, macho.REBASE_TYPE_POINTER)); + try writer.writeByte(macho.REBASE_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB | @truncate(u4, symbol.segment)); + try leb.writeILEB128(writer, symbol.offset); + try writer.writeByte(macho.REBASE_OPCODE_DO_REBASE_IMM_TIMES | @truncate(u4, 1)); + } + try writer.writeByte(macho.REBASE_OPCODE_DONE); +} - /// Addend value (if any). - addend: ?i64 = null, - }; +pub fn bindInfoSize(symbols: []*const ExternSymbol) !u64 { + var stream = std.io.countingWriter(std.io.null_writer); + var writer = stream.writer(); + var size: u64 = 0; - pub fn deinit(self: *BindingInfoTable, allocator: *Allocator) void { - for (self.symbols.items) |*symbol| { - if (symbol.name) |name| { - allocator.free(name); - } + for (symbols) |symbol| { + size += 1; + if (symbol.dylib_ordinal > 15) { + try leb.writeULEB128(writer, @bitCast(u64, symbol.dylib_ordinal)); } - self.symbols.deinit(allocator); - } + size += 1; - /// Parse the binding info table from byte stream. - pub fn read(self: *BindingInfoTable, reader: anytype, allocator: *Allocator) !void { - var symbol: Symbol = .{ - .segment = 0, - .offset = 0, - }; + if (symbol.name) |name| { + size += 1; + size += name.len; + size += 1; + } - var dylib_ordinal_set = false; - var done = false; - while (true) { - const inst = reader.readByte() catch |err| switch (err) { - error.EndOfStream => break, - else => return err, - }; - const imm: u8 = inst & macho.BIND_IMMEDIATE_MASK; - const opcode: u8 = inst & macho.BIND_OPCODE_MASK; + size += 1; + try leb.writeILEB128(writer, symbol.offset); - switch (opcode) { - macho.BIND_OPCODE_DO_BIND => { - try self.symbols.append(allocator, symbol); - symbol = .{ - .segment = 0, - .offset = 0, - }; - }, - macho.BIND_OPCODE_DONE => { - done = true; - break; - }, - macho.BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM => { - var name = std.ArrayList(u8).init(allocator); - var next = try reader.readByte(); - while (next != @as(u8, 0)) { - try name.append(next); - next = try reader.readByte(); - } - symbol.name = name.toOwnedSlice(); - }, - macho.BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB => { - symbol.segment = imm; - symbol.offset = try leb.readILEB128(i64, reader); - }, - macho.BIND_OPCODE_SET_DYLIB_SPECIAL_IMM, macho.BIND_OPCODE_SET_DYLIB_ORDINAL_IMM => { - assert(!dylib_ordinal_set); - self.dylib_ordinal = imm; - }, - macho.BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB => { - assert(!dylib_ordinal_set); - self.dylib_ordinal = try leb.readILEB128(i64, reader); - }, - macho.BIND_OPCODE_SET_TYPE_IMM => { - self.binding_type = imm; - }, - macho.BIND_OPCODE_SET_ADDEND_SLEB => { - symbol.addend = try leb.readILEB128(i64, reader); - }, - else => { - std.log.warn("unhandled BIND_OPCODE_: 0x{x}", .{opcode}); - }, - } + if (symbol.addend) |addend| { + size += 1; + try leb.writeILEB128(writer, addend); } - assert(done); + + size += 2; } - /// Write the binding info table to byte stream. - pub fn write(self: BindingInfoTable, writer: anytype) !void { - if (self.dylib_ordinal > 15) { + size += stream.bytes_written; + return size; +} + +pub fn writeBindInfo(symbols: []*const ExternSymbol, writer: anytype) !void { + for (symbols) |symbol| { + if (symbol.dylib_ordinal > 15) { try writer.writeByte(macho.BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB); - try leb.writeULEB128(writer, @bitCast(u64, self.dylib_ordinal)); - } else if (self.dylib_ordinal > 0) { - try writer.writeByte(macho.BIND_OPCODE_SET_DYLIB_ORDINAL_IMM | @truncate(u4, @bitCast(u64, self.dylib_ordinal))); + try leb.writeULEB128(writer, @bitCast(u64, symbol.dylib_ordinal)); + } else if (symbol.dylib_ordinal > 0) { + try writer.writeByte(macho.BIND_OPCODE_SET_DYLIB_ORDINAL_IMM | @truncate(u4, @bitCast(u64, symbol.dylib_ordinal))); } else { - try writer.writeByte(macho.BIND_OPCODE_SET_DYLIB_SPECIAL_IMM | @truncate(u4, @bitCast(u64, self.dylib_ordinal))); + try writer.writeByte(macho.BIND_OPCODE_SET_DYLIB_SPECIAL_IMM | @truncate(u4, @bitCast(u64, symbol.dylib_ordinal))); } - try writer.writeByte(macho.BIND_OPCODE_SET_TYPE_IMM | @truncate(u4, self.binding_type)); - - for (self.symbols.items) |symbol| { - if (symbol.name) |name| { - try writer.writeByte(macho.BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM); // TODO Sometimes we might want to add flags. - try writer.writeAll(name); - try writer.writeByte(0); - } + try writer.writeByte(macho.BIND_OPCODE_SET_TYPE_IMM | @truncate(u4, macho.BIND_TYPE_POINTER)); - try writer.writeByte(macho.BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB | @truncate(u4, symbol.segment)); - try leb.writeILEB128(writer, symbol.offset); + if (symbol.name) |name| { + try writer.writeByte(macho.BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM); // TODO Sometimes we might want to add flags. + try writer.writeAll(name); + try writer.writeByte(0); + } - if (symbol.addend) |addend| { - try writer.writeByte(macho.BIND_OPCODE_SET_ADDEND_SLEB); - try leb.writeILEB128(writer, addend); - } + try writer.writeByte(macho.BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB | @truncate(u4, symbol.segment)); + try leb.writeILEB128(writer, symbol.offset); - try writer.writeByte(macho.BIND_OPCODE_DO_BIND); + if (symbol.addend) |addend| { + try writer.writeByte(macho.BIND_OPCODE_SET_ADDEND_SLEB); + try leb.writeILEB128(writer, addend); } + try writer.writeByte(macho.BIND_OPCODE_DO_BIND); try writer.writeByte(macho.BIND_OPCODE_DONE); } +} - /// Calculate size in bytes of this binding info table. - pub fn calcSize(self: *BindingInfoTable) !u64 { - var stream = std.io.countingWriter(std.io.null_writer); - var writer = stream.writer(); - var size: u64 = 1; - - if (self.dylib_ordinal > 15) { - try leb.writeULEB128(writer, @bitCast(u64, self.dylib_ordinal)); - } +pub fn lazyBindInfoSize(symbols: []*const ExternSymbol) !u64 { + var stream = std.io.countingWriter(std.io.null_writer); + var writer = stream.writer(); + var size: u64 = 0; + for (symbols) |symbol| { size += 1; + try leb.writeILEB128(writer, symbol.offset); - for (self.symbols.items) |symbol| { - if (symbol.name) |name| { - size += 1; - size += name.len; - size += 1; - } - - size += 1; - try leb.writeILEB128(writer, symbol.offset); - - if (symbol.addend) |addend| { - size += 1; - try leb.writeILEB128(writer, addend); - } - + if (symbol.addend) |addend| { size += 1; + try leb.writeILEB128(writer, addend); } - size += 1 + stream.bytes_written; - return size; - } -}; - -/// Table of lazy binding info entries used to tell the dyld which -/// symbols to lazily bind at first load of a dylib. -pub const LazyBindingInfoTable = struct { - symbols: std.ArrayListUnmanaged(Symbol) = .{}, - - pub const Symbol = struct { - /// Symbol name. - name: ?[]u8 = null, - - /// Offset of this symbol wrt to the segment id encoded in `segment`. - offset: i64, - - /// Id of the dylib where this symbol is expected to reside. - /// Positive ordinals point at dylibs imported with LC_LOAD_DYLIB, - /// 0 means this binary, -1 the main executable, and -2 flat lookup. - dylib_ordinal: i64, - - /// Id of the segment where to bind this symbol to. - segment: u8, - - /// Addend value (if any). - addend: ?i64 = null, - }; - - pub fn deinit(self: *LazyBindingInfoTable, allocator: *Allocator) void { - for (self.symbols.items) |*symbol| { - if (symbol.name) |name| { - allocator.free(name); - } + size += 1; + if (symbol.dylib_ordinal > 15) { + try leb.writeULEB128(writer, @bitCast(u64, symbol.dylib_ordinal)); } - self.symbols.deinit(allocator); - } - - /// Parse the binding info table from byte stream. - pub fn read(self: *LazyBindingInfoTable, reader: anytype, allocator: *Allocator) !void { - var symbol: Symbol = .{ - .offset = 0, - .segment = 0, - .dylib_ordinal = 0, - }; - - var done = false; - while (true) { - const inst = reader.readByte() catch |err| switch (err) { - error.EndOfStream => break, - else => return err, - }; - const imm: u8 = inst & macho.BIND_IMMEDIATE_MASK; - const opcode: u8 = inst & macho.BIND_OPCODE_MASK; - - switch (opcode) { - macho.BIND_OPCODE_DO_BIND => { - try self.symbols.append(allocator, symbol); - }, - macho.BIND_OPCODE_DONE => { - done = true; - symbol = .{ - .offset = 0, - .segment = 0, - .dylib_ordinal = 0, - }; - }, - macho.BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM => { - var name = std.ArrayList(u8).init(allocator); - var next = try reader.readByte(); - while (next != @as(u8, 0)) { - try name.append(next); - next = try reader.readByte(); - } - symbol.name = name.toOwnedSlice(); - }, - macho.BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB => { - symbol.segment = imm; - symbol.offset = try leb.readILEB128(i64, reader); - }, - macho.BIND_OPCODE_SET_DYLIB_SPECIAL_IMM, macho.BIND_OPCODE_SET_DYLIB_ORDINAL_IMM => { - symbol.dylib_ordinal = imm; - }, - macho.BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB => { - symbol.dylib_ordinal = try leb.readILEB128(i64, reader); - }, - macho.BIND_OPCODE_SET_ADDEND_SLEB => { - symbol.addend = try leb.readILEB128(i64, reader); - }, - else => { - std.log.warn("unhandled BIND_OPCODE_: 0x{x}", .{opcode}); - }, - } + if (symbol.name) |name| { + size += 1; + size += name.len; + size += 1; } - assert(done); + size += 2; } - /// Write the binding info table to byte stream. - pub fn write(self: LazyBindingInfoTable, writer: anytype) !void { - for (self.symbols.items) |symbol| { - try writer.writeByte(macho.BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB | @truncate(u4, symbol.segment)); - try leb.writeILEB128(writer, symbol.offset); - - if (symbol.addend) |addend| { - try writer.writeByte(macho.BIND_OPCODE_SET_ADDEND_SLEB); - try leb.writeILEB128(writer, addend); - } + size += stream.bytes_written; + return size; +} - if (symbol.dylib_ordinal > 15) { - try writer.writeByte(macho.BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB); - try leb.writeULEB128(writer, @bitCast(u64, symbol.dylib_ordinal)); - } else if (symbol.dylib_ordinal > 0) { - try writer.writeByte(macho.BIND_OPCODE_SET_DYLIB_ORDINAL_IMM | @truncate(u4, @bitCast(u64, symbol.dylib_ordinal))); - } else { - try writer.writeByte(macho.BIND_OPCODE_SET_DYLIB_SPECIAL_IMM | @truncate(u4, @bitCast(u64, symbol.dylib_ordinal))); - } +pub fn writeLazyBindInfo(symbols: []*const ExternSymbol, writer: anytype) !void { + for (symbols) |symbol| { + try writer.writeByte(macho.BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB | @truncate(u4, symbol.segment)); + try leb.writeILEB128(writer, symbol.offset); - if (symbol.name) |name| { - try writer.writeByte(macho.BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM); // TODO Sometimes we might want to add flags. - try writer.writeAll(name); - try writer.writeByte(0); - } - - try writer.writeByte(macho.BIND_OPCODE_DO_BIND); - try writer.writeByte(macho.BIND_OPCODE_DONE); + if (symbol.addend) |addend| { + try writer.writeByte(macho.BIND_OPCODE_SET_ADDEND_SLEB); + try leb.writeILEB128(writer, addend); } - } - /// Calculate size in bytes of this binding info table. - pub fn calcSize(self: *LazyBindingInfoTable) !u64 { - var stream = std.io.countingWriter(std.io.null_writer); - var writer = stream.writer(); - var size: u64 = 0; - - for (self.symbols.items) |symbol| { - size += 1; - try leb.writeILEB128(writer, symbol.offset); - - if (symbol.addend) |addend| { - size += 1; - try leb.writeILEB128(writer, addend); - } + if (symbol.dylib_ordinal > 15) { + try writer.writeByte(macho.BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB); + try leb.writeULEB128(writer, @bitCast(u64, symbol.dylib_ordinal)); + } else if (symbol.dylib_ordinal > 0) { + try writer.writeByte(macho.BIND_OPCODE_SET_DYLIB_ORDINAL_IMM | @truncate(u4, @bitCast(u64, symbol.dylib_ordinal))); + } else { + try writer.writeByte(macho.BIND_OPCODE_SET_DYLIB_SPECIAL_IMM | @truncate(u4, @bitCast(u64, symbol.dylib_ordinal))); + } - size += 1; - if (symbol.dylib_ordinal > 15) { - try leb.writeULEB128(writer, @bitCast(u64, symbol.dylib_ordinal)); - } - if (symbol.name) |name| { - size += 1; - size += name.len; - size += 1; - } - size += 2; + if (symbol.name) |name| { + try writer.writeByte(macho.BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM); // TODO Sometimes we might want to add flags. + try writer.writeAll(name); + try writer.writeByte(0); } - size += stream.bytes_written; - return size; + try writer.writeByte(macho.BIND_OPCODE_DO_BIND); + try writer.writeByte(macho.BIND_OPCODE_DONE); } -}; +} -- cgit v1.2.3 From 7d40aaad2b514703995458909b315f222543c4cd Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Sat, 9 Jan 2021 20:38:55 +0100 Subject: macho: document more code + add test case --- src/codegen.zig | 23 ++++++++--------- src/link/MachO.zig | 62 +++++++++++++++++++++++++++++----------------- src/link/MachO/imports.zig | 31 ++++------------------- test/stage2/aarch64.zig | 17 +++++++++++++ 4 files changed, 71 insertions(+), 62 deletions(-) (limited to 'src/codegen.zig') diff --git a/src/codegen.zig b/src/codegen.zig index ad4215191f..05898c77c8 100644 --- a/src/codegen.zig +++ b/src/codegen.zig @@ -1865,19 +1865,16 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { // If it doesn't, it will get autofreed when we clean up the extern symbol table. const decl_name = try std.fmt.allocPrint(self.bin_file.allocator, "_{s}", .{decl.name}); const already_defined = macho_file.extern_lazy_symbols.contains(decl_name); - const symbol: u32 = blk: { - if (macho_file.extern_lazy_symbols.get(decl_name)) |sym| { - self.bin_file.allocator.free(decl_name); - break :blk sym.index; - } else { - const index = @intCast(u32, macho_file.extern_lazy_symbols.items().len); - try macho_file.extern_lazy_symbols.putNoClobber(self.bin_file.allocator, decl_name, .{ - .name = decl_name, - .dylib_ordinal = 1, // TODO this is now hardcoded, since we only support libSystem. - .index = index, - }); - break :blk index; - } + const symbol: u32 = if (macho_file.extern_lazy_symbols.getIndex(decl_name)) |index| blk: { + self.bin_file.allocator.free(decl_name); + break :blk @intCast(u32, index); + } else blk: { + const index = @intCast(u32, macho_file.extern_lazy_symbols.items().len); + try macho_file.extern_lazy_symbols.putNoClobber(self.bin_file.allocator, decl_name, .{ + .name = decl_name, + .dylib_ordinal = 1, // TODO this is now hardcoded, since we only support libSystem. + }); + break :blk index; }; try macho_file.stub_fixups.append(self.bin_file.allocator, .{ .symbol = symbol, diff --git a/src/link/MachO.zig b/src/link/MachO.zig index 7d86c27aa9..c3808911eb 100644 --- a/src/link/MachO.zig +++ b/src/link/MachO.zig @@ -159,19 +159,29 @@ last_text_block: ?*TextBlock = null, /// prior to calling `generateSymbol`, and then immediately deallocated /// rather than sitting in the global scope. pie_fixups: std.ArrayListUnmanaged(PieFixup) = .{}, - +/// A list of all stub (extern decls) fixups required for this run of the linker. +/// Warning, this is currently NOT thread-safe. See the TODO below. +/// TODO Move this list inside `updateDecl` where it should be allocated +/// prior to calling `generateSymbol`, and then immediately deallocated +/// rather than sitting in the global scope. stub_fixups: std.ArrayListUnmanaged(StubFixup) = .{}, -pub const StubFixup = struct { - symbol: u32, - already_defined: bool, +pub const PieFixup = struct { + /// Target address we wanted to address in absolute terms. + address: u64, + /// Where in the byte stream we should perform the fixup. start: usize, + /// The length of the byte stream. For x86_64, this will be + /// variable. For aarch64, it will be fixed at 4 bytes. len: usize, }; -pub const PieFixup = struct { - /// Target address we wanted to address in absolute terms. - address: u64, +pub const StubFixup = struct { + /// Id of extern (lazy) symbol. + symbol: u32, + /// Signals whether the symbol has already been declared before. If so, + /// then there is no need to rewrite the stub entry and related. + already_defined: bool, /// Where in the byte stream we should perform the fixup. start: usize, /// The length of the byte stream. For x86_64, this will be @@ -1030,13 +1040,20 @@ pub fn deinit(self: *MachO) void { if (self.d_sym) |*ds| { ds.deinit(self.base.allocator); } + for (self.extern_lazy_symbols.items()) |*entry| { + entry.value.deinit(self.base.allocator); + } + self.extern_lazy_symbols.deinit(self.base.allocator); + for (self.extern_nonlazy_symbols.items()) |*entry| { + entry.value.deinit(self.base.allocator); + } + self.extern_nonlazy_symbols.deinit(self.base.allocator); self.pie_fixups.deinit(self.base.allocator); + self.stub_fixups.deinit(self.base.allocator); self.text_block_free_list.deinit(self.base.allocator); self.offset_table.deinit(self.base.allocator); self.offset_table_free_list.deinit(self.base.allocator); self.string_table.deinit(self.base.allocator); - self.extern_lazy_symbols.deinit(self.base.allocator); - self.extern_nonlazy_symbols.deinit(self.base.allocator); self.global_symbols.deinit(self.base.allocator); self.global_symbol_free_list.deinit(self.base.allocator); self.local_symbols.deinit(self.base.allocator); @@ -2047,7 +2064,6 @@ pub fn populateMissingMetadata(self: *MachO) !void { try self.extern_nonlazy_symbols.putNoClobber(self.base.allocator, name, .{ .name = name, .dylib_ordinal = 1, // TODO this is currently hardcoded. - .index = index, .segment = self.data_const_segment_cmd_index.?, .offset = index * @sizeOf(u64), }); @@ -2582,12 +2598,12 @@ fn writeAllGlobalAndUndefSymbols(self: *MachO) !void { } fn writeIndirectSymbolTable(self: *MachO) !void { - const text_seg = &self.load_commands.items[self.text_segment_cmd_index.?].Segment; - const stubs = &text_seg.sections.items[self.stubs_section_index.?]; - const dc_seg = &self.load_commands.items[self.data_const_segment_cmd_index.?].Segment; - const got = &dc_seg.sections.items[self.data_got_section_index.?]; - const data_seg = &self.load_commands.items[self.data_segment_cmd_index.?].Segment; - const la = &data_seg.sections.items[self.la_symbol_ptr_section_index.?]; + const text_segment = &self.load_commands.items[self.text_segment_cmd_index.?].Segment; + const stubs = &text_segment.sections.items[self.stubs_section_index.?]; + const data_const_seg = &self.load_commands.items[self.data_const_segment_cmd_index.?].Segment; + const got = &data_const_seg.sections.items[self.data_got_section_index.?]; + 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 dysymtab = &self.load_commands.items[self.dysymtab_cmd_index.?].Dysymtab; dysymtab.nindirectsyms = 0; @@ -2595,8 +2611,8 @@ fn writeIndirectSymbolTable(self: *MachO) !void { var off = dysymtab.indirectsymoff; stubs.reserved1 = 0; - for (self.extern_lazy_symbols.items()) |entry| { - const symtab_idx = @intCast(u32, dysymtab.iundefsym + entry.value.index); + for (self.extern_lazy_symbols.items()) |_, i| { + const symtab_idx = @intCast(u32, dysymtab.iundefsym + i); mem.writeIntLittle(u32, &buf, symtab_idx); try self.base.file.?.pwriteAll(&buf, off); off += @sizeOf(u32); @@ -2605,17 +2621,17 @@ fn writeIndirectSymbolTable(self: *MachO) !void { const base_id = @intCast(u32, self.extern_lazy_symbols.items().len); got.reserved1 = base_id; - for (self.extern_nonlazy_symbols.items()) |entry| { - const symtab_idx = @intCast(u32, dysymtab.iundefsym + entry.value.index + base_id); + for (self.extern_nonlazy_symbols.items()) |_, i| { + const symtab_idx = @intCast(u32, dysymtab.iundefsym + i + base_id); mem.writeIntLittle(u32, &buf, symtab_idx); try self.base.file.?.pwriteAll(&buf, off); off += @sizeOf(u32); dysymtab.nindirectsyms += 1; } - la.reserved1 = got.reserved1 + @intCast(u32, self.extern_nonlazy_symbols.items().len); - for (self.extern_lazy_symbols.items()) |entry| { - const symtab_idx = @intCast(u32, dysymtab.iundefsym + entry.value.index); + la_symbol_ptr.reserved1 = got.reserved1 + @intCast(u32, self.extern_nonlazy_symbols.items().len); + for (self.extern_lazy_symbols.items()) |_, i| { + const symtab_idx = @intCast(u32, dysymtab.iundefsym + i); mem.writeIntLittle(u32, &buf, symtab_idx); try self.base.file.?.pwriteAll(&buf, off); off += @sizeOf(u32); diff --git a/src/link/MachO/imports.zig b/src/link/MachO/imports.zig index 8e0f72e1de..c5f6211f1a 100644 --- a/src/link/MachO/imports.zig +++ b/src/link/MachO/imports.zig @@ -20,13 +20,15 @@ pub const ExternSymbol = struct { /// dylibs. dylib_ordinal: i64 = 0, + /// Id of the segment where this symbol is defined (will have its address + /// resolved). segment: u16 = 0, + + /// Offset relative to the start address of the `segment`. offset: u32 = 0, - addend: ?i32 = null, - index: u32, pub fn deinit(self: *ExternSymbol, allocator: *Allocator) void { - if (self.name) |*name| { + if (self.name) |name| { allocator.free(name); } } @@ -77,12 +79,6 @@ pub fn bindInfoSize(symbols: []*const ExternSymbol) !u64 { size += 1; try leb.writeILEB128(writer, symbol.offset); - - if (symbol.addend) |addend| { - size += 1; - try leb.writeILEB128(writer, addend); - } - size += 2; } @@ -110,12 +106,6 @@ pub fn writeBindInfo(symbols: []*const ExternSymbol, writer: anytype) !void { try writer.writeByte(macho.BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB | @truncate(u4, symbol.segment)); try leb.writeILEB128(writer, symbol.offset); - - if (symbol.addend) |addend| { - try writer.writeByte(macho.BIND_OPCODE_SET_ADDEND_SLEB); - try leb.writeILEB128(writer, addend); - } - try writer.writeByte(macho.BIND_OPCODE_DO_BIND); try writer.writeByte(macho.BIND_OPCODE_DONE); } @@ -129,12 +119,6 @@ pub fn lazyBindInfoSize(symbols: []*const ExternSymbol) !u64 { for (symbols) |symbol| { size += 1; try leb.writeILEB128(writer, symbol.offset); - - if (symbol.addend) |addend| { - size += 1; - try leb.writeILEB128(writer, addend); - } - size += 1; if (symbol.dylib_ordinal > 15) { try leb.writeULEB128(writer, @bitCast(u64, symbol.dylib_ordinal)); @@ -156,11 +140,6 @@ pub fn writeLazyBindInfo(symbols: []*const ExternSymbol, writer: anytype) !void try writer.writeByte(macho.BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB | @truncate(u4, symbol.segment)); try leb.writeILEB128(writer, symbol.offset); - if (symbol.addend) |addend| { - try writer.writeByte(macho.BIND_OPCODE_SET_ADDEND_SLEB); - try leb.writeILEB128(writer, addend); - } - if (symbol.dylib_ordinal > 15) { try writer.writeByte(macho.BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB); try leb.writeULEB128(writer, @bitCast(u64, symbol.dylib_ordinal)); diff --git a/test/stage2/aarch64.zig b/test/stage2/aarch64.zig index 7d05c60bb8..1dde30e969 100644 --- a/test/stage2/aarch64.zig +++ b/test/stage2/aarch64.zig @@ -199,4 +199,21 @@ pub fn addCases(ctx: *TestContext) !void { "", ); } + + { + var case = ctx.exe("hello world linked to libc", macos_aarch64); + + // TODO rewrite this test once we handle more int conversions and return args. + case.addCompareOutput( + \\extern "c" fn write(usize, usize, usize) void; + \\extern "c" fn exit(usize) noreturn; + \\ + \\export fn _start() noreturn { + \\ write(1, @ptrToInt("Hello, World!\n"), 14); + \\ exit(0); + \\} + , + "Hello, World!\n", + ); + } } -- cgit v1.2.3 From f0d7ec6f33634edb0ddb3ba5d5b306e9f2de5418 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Sun, 10 Jan 2021 00:21:34 +0100 Subject: macho: add x86_64 support --- src/codegen.zig | 22 +++++++++++-- src/link/MachO.zig | 91 ++++++++++++++++++++++++++++++++++++++++++++---------- 2 files changed, 93 insertions(+), 20 deletions(-) (limited to 'src/codegen.zig') diff --git a/src/codegen.zig b/src/codegen.zig index 05898c77c8..bfb1540e40 100644 --- a/src/codegen.zig +++ b/src/codegen.zig @@ -1876,14 +1876,30 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { }); break :blk index; }; + const start = self.code.items.len; + const len: usize = blk: { + switch (arch) { + .x86_64 => { + // callq + try self.code.ensureCapacity(self.code.items.len + 5); + self.code.appendSliceAssumeCapacity(&[5]u8{ 0xe8, 0x0, 0x0, 0x0, 0x0 }); + break :blk 5; + }, + .aarch64 => { + // bl + writeInt(u32, try self.code.addManyAsArray(4), 0); + break :blk 4; + }, + else => unreachable, // unsupported architecture on MachO + } + }; try macho_file.stub_fixups.append(self.bin_file.allocator, .{ .symbol = symbol, .already_defined = already_defined, - .start = self.code.items.len, - .len = 4, + .start = start, + .len = len, }); // We mark the space and fix it up later. - writeInt(u32, try self.code.addManyAsArray(4), 0); } else { return self.fail(inst.base.src, "TODO implement calling bitcasted functions", .{}); } diff --git a/src/link/MachO.zig b/src/link/MachO.zig index 3839158f70..fed3ee7836 100644 --- a/src/link/MachO.zig +++ b/src/link/MachO.zig @@ -1241,14 +1241,18 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void { for (self.stub_fixups.items) |fixup| { const stub_addr = stubs.addr + fixup.symbol * stubs.reserved2; const text_addr = symbol.n_value + fixup.start; - const displacement = @intCast(u32, stub_addr - text_addr); - var placeholder = code_buffer.items[fixup.start..][0..fixup.len]; switch (self.base.options.target.cpu.arch) { - .x86_64 => return error.TODOImplementStubFixupsForx86_64, + .x86_64 => { + const displacement = @intCast(u32, stub_addr - text_addr - fixup.len); + var placeholder = code_buffer.items[fixup.start + fixup.len - @sizeOf(u32) ..][0..@sizeOf(u32)]; + mem.writeIntSliceLittle(u32, placeholder, displacement); + }, .aarch64 => { + const displacement = @intCast(u32, stub_addr - text_addr); + var placeholder = code_buffer.items[fixup.start..][0..fixup.len]; mem.writeIntSliceLittle(u32, placeholder, aarch64.Instruction.bl(@intCast(i28, displacement)).toU32()); }, - else => unreachable, + else => unreachable, // unsupported target architecture } if (!fixup.already_defined) { try self.writeStub(fixup.symbol); @@ -1565,6 +1569,11 @@ pub fn populateMissingMetadata(self: *MachO) !void { .aarch64 => 2, else => unreachable, // unhandled architecture type }; + const stub_size: u4 = switch (self.base.options.target.cpu.arch) { + .x86_64 => 6, + .aarch64 => 2 * @sizeOf(u32), + else => unreachable, // unhandled architecture type + }; const flags = macho.S_SYMBOL_STUBS | macho.S_ATTR_PURE_INSTRUCTIONS | macho.S_ATTR_SOME_INSTRUCTIONS; const needed_size = @sizeOf(u64) * self.base.options.symbol_count_hint; const off = text_segment.findFreeSpace(needed_size, @alignOf(u64), self.header_pad); @@ -1583,7 +1592,7 @@ pub fn populateMissingMetadata(self: *MachO) !void { .nreloc = 0, .flags = flags, .reserved1 = 0, - .reserved2 = 2 * @sizeOf(u32), + .reserved2 = stub_size, .reserved3 = 0, }); self.header_dirty = true; @@ -2044,7 +2053,30 @@ pub fn populateMissingMetadata(self: *MachO) !void { const data_const_segment = &self.load_commands.items[self.data_const_segment_cmd_index.?].Segment; const got = &data_const_segment.sections.items[self.data_got_section_index.?]; switch (self.base.options.target.cpu.arch) { - .x86_64 => return error.TODOImplementStubHelperForX86_64, + .x86_64 => { + const code_size = 15; + var code: [code_size]u8 = undefined; + // lea %r11, [rip + disp] + code[0] = 0x4c; + code[1] = 0x8d; + code[2] = 0x1d; + { + const displacement = @intCast(u32, data.addr - stub_helper.addr - 7); + mem.writeIntLittle(u32, code[3..7], displacement); + } + // push %r11 + code[7] = 0x41; + code[8] = 0x53; + // jmp [rip + disp] + code[9] = 0xff; + code[10] = 0x25; + { + const displacement = @intCast(u32, got.addr - stub_helper.addr - code_size); + mem.writeIntLittle(u32, code[11..], displacement); + } + self.stub_helper_stubs_start_off = stub_helper.offset + code_size; + try self.base.file.?.pwriteAll(&code, stub_helper.offset); + }, .aarch64 => { var code: [4 * @sizeOf(u32)]u8 = undefined; { @@ -2410,7 +2442,12 @@ fn writeLazySymbolPointer(self: *MachO, index: u32) !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 stub_off = self.stub_helper_stubs_start_off.? + index * 3 * @sizeOf(u32); + 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; const end = stub_helper.addr + stub_off - stub_helper.offset; var buf: [@sizeOf(u64)]u8 = undefined; mem.writeIntLittle(u64, &buf, end); @@ -2428,42 +2465,62 @@ fn writeStub(self: *MachO, index: u32) !void { const stub_off = stubs.offset + index * stubs.reserved2; const stub_addr = stubs.addr + index * stubs.reserved2; const la_ptr_addr = la_symbol_ptr.addr + index * @sizeOf(u64); - const displacement = la_ptr_addr - stub_addr; log.debug("writing stub at 0x{x}", .{stub_off}); + var code = try self.base.allocator.alloc(u8, stubs.reserved2); + defer self.base.allocator.free(code); switch (self.base.options.target.cpu.arch) { - .x86_64 => return error.TODOImplementWritingStubsForx86_64, + .x86_64 => { + const displacement = @intCast(u32, la_ptr_addr - stub_addr - stubs.reserved2); + // jmp + code[0] = 0xff; + code[1] = 0x25; + mem.writeIntLittle(u32, code[2..][0..4], displacement); + }, .aarch64 => { - var code: [2 * @sizeOf(u32)]u8 = undefined; + const displacement = la_ptr_addr - stub_addr; mem.writeIntLittle(u32, code[0..4], aarch64.Instruction.ldr(.x16, .{ .literal = @intCast(u19, displacement / 4), }).toU32()); mem.writeIntLittle(u32, code[4..8], aarch64.Instruction.br(.x16).toU32()); - try self.base.file.?.pwriteAll(&code, stub_off); }, else => unreachable, } + 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_off = self.stub_helper_stubs_start_off.? + index * 3 * @sizeOf(u32); - const end = stub_helper.addr + stub_off - stub_helper.offset; - const displacement = @intCast(i64, stub_helper.addr) - @intCast(i64, end + 4); + 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 => return error.TODOImplementWritingStubsInStubHelperForx86_64, + .x86_64 => { + const displacement = @intCast(i32, @intCast(i64, stub_helper.offset) - @intCast(i64, stub_off) - stub_size); + // pushq + code[0] = 0x68; + mem.writeIntLittle(u32, code[1..][0..4], index * 0xd); // TODO + // jmpq + code[5] = 0xe9; + mem.writeIntLittle(u32, code[6..][0..4], @bitCast(u32, displacement)); + }, .aarch64 => { - var code: [3 * @sizeOf(u32)]u8 = undefined; + const displacement = @intCast(i64, stub_helper.offset) - @intCast(i64, stub_off) - 4; mem.writeIntLittle(u32, code[0..4], aarch64.Instruction.ldr(.w16, .{ .literal = 0x2, }).toU32()); mem.writeIntLittle(u32, code[4..8], aarch64.Instruction.b(@intCast(i28, displacement)).toU32()); mem.writeIntLittle(u32, code[8..12], index * 0xd); // TODO This is the size of lazy binding opcode block. - try self.base.file.?.pwriteAll(&code, stub_off); }, else => unreachable, } + try self.base.file.?.pwriteAll(code, stub_off); } fn relocateSymbolTable(self: *MachO) !void { -- cgit v1.2.3