diff options
| author | Jakub Konka <kubkon@jakubkonka.com> | 2021-01-09 19:29:33 +0100 |
|---|---|---|
| committer | Jakub Konka <kubkon@jakubkonka.com> | 2021-01-13 23:55:06 +0100 |
| commit | b86d0e488b9dfc7d943da86a34998360e24da225 (patch) | |
| tree | 7dd75cbe0d1ce9c8a4daa5296978aa7ba687a71e /src/link | |
| parent | 21c7217e09b48bf3bf9656fb1e8e69b85422b02e (diff) | |
| download | zig-b86d0e488b9dfc7d943da86a34998360e24da225.tar.gz zig-b86d0e488b9dfc7d943da86a34998360e24da225.zip | |
macho: refactor writing and managing externs
Diffstat (limited to 'src/link')
| -rw-r--r-- | src/link/MachO.zig | 383 | ||||
| -rw-r--r-- | src/link/MachO/imports.zig | 440 |
2 files changed, 347 insertions, 476 deletions
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); } -}; +} |
