diff options
| author | Jakub Konka <kubkon@jakubkonka.com> | 2023-10-30 17:29:05 +0100 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2023-10-30 17:29:05 +0100 |
| commit | 10d03acdb5ff81c112280854629c9f9032e14330 (patch) | |
| tree | ed9c38b4c3c6ff17a1eaecacce35114219005bdb /src | |
| parent | 91e117697ad90430d9266203415712b6cc59f669 (diff) | |
| parent | 324a93e673afcf1bcaac1163379d385952e52a27 (diff) | |
| download | zig-10d03acdb5ff81c112280854629c9f9032e14330.tar.gz zig-10d03acdb5ff81c112280854629c9f9032e14330.zip | |
Merge pull request #17773 from ziglang/elf-exports
link: implement exporting anon decls
Diffstat (limited to 'src')
| -rw-r--r-- | src/arch/x86_64/CodeGen.zig | 6 | ||||
| -rw-r--r-- | src/arch/x86_64/Emit.zig | 28 | ||||
| -rw-r--r-- | src/codegen.zig | 20 | ||||
| -rw-r--r-- | src/link/Coff.zig | 132 | ||||
| -rw-r--r-- | src/link/Elf.zig | 117 | ||||
| -rw-r--r-- | src/link/MachO.zig | 232 | ||||
| -rw-r--r-- | src/link/MachO/Atom.zig | 47 | ||||
| -rw-r--r-- | src/link/MachO/Relocation.zig | 4 | ||||
| -rw-r--r-- | src/link/MachO/zld.zig | 4 |
9 files changed, 365 insertions, 225 deletions
diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index 18011fc054..318b1bf670 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/src/arch/x86_64/CodeGen.zig @@ -13063,6 +13063,7 @@ fn genExternSymbolRef( } }, }); } else if (self.bin_file.cast(link.File.Coff)) |coff_file| { + const global_index = try coff_file.getGlobalSymbol(callee, lib); _ = try self.addInst(.{ .tag = .mov, .ops = .import_reloc, @@ -13070,7 +13071,7 @@ fn genExternSymbolRef( .r1 = .rax, .payload = try self.addExtra(bits.Symbol{ .atom_index = atom_index, - .sym_index = try coff_file.getGlobalSymbol(callee, lib), + .sym_index = link.File.Coff.global_symbol_bit | global_index, }), } }, }); @@ -13080,12 +13081,13 @@ fn genExternSymbolRef( else => unreachable, } } else if (self.bin_file.cast(link.File.MachO)) |macho_file| { + const global_index = try macho_file.getGlobalSymbol(callee, lib); _ = try self.addInst(.{ .tag = .call, .ops = .extern_fn_reloc, .data = .{ .reloc = .{ .atom_index = atom_index, - .sym_index = try macho_file.getGlobalSymbol(callee, lib), + .sym_index = link.File.MachO.global_symbol_bit | global_index, } }, }); } else return self.fail("TODO implement calling extern functions", .{}); diff --git a/src/arch/x86_64/Emit.zig b/src/arch/x86_64/Emit.zig index 38ac99819a..ca796a4d71 100644 --- a/src/arch/x86_64/Emit.zig +++ b/src/arch/x86_64/Emit.zig @@ -52,7 +52,10 @@ pub fn emitMir(emit: *Emit) Error!void { // Add relocation to the decl. const atom_index = macho_file.getAtomIndexForSymbol(.{ .sym_index = symbol.atom_index }).?; - const target = macho_file.getGlobalByIndex(symbol.sym_index); + const target = if (link.File.MachO.global_symbol_bit & symbol.sym_index != 0) + macho_file.getGlobalByIndex(link.File.MachO.global_symbol_mask & symbol.sym_index) + else + link.File.MachO.SymbolWithLoc{ .sym_index = symbol.sym_index }; try link.File.MachO.Atom.addRelocation(macho_file, atom_index, .{ .type = .branch, .target = target, @@ -66,7 +69,10 @@ pub fn emitMir(emit: *Emit) Error!void { const atom_index = coff_file.getAtomIndexForSymbol( .{ .sym_index = symbol.atom_index, .file = null }, ).?; - const target = coff_file.getGlobalByIndex(symbol.sym_index); + const target = if (link.File.Coff.global_symbol_bit & symbol.sym_index != 0) + coff_file.getGlobalByIndex(link.File.Coff.global_symbol_mask & symbol.sym_index) + else + link.File.Coff.SymbolWithLoc{ .sym_index = symbol.sym_index, .file = null }; try link.File.Coff.Atom.addRelocation(coff_file, atom_index, .{ .type = .direct, .target = target, @@ -116,6 +122,10 @@ pub fn emitMir(emit: *Emit) Error!void { } else if (emit.lower.bin_file.cast(link.File.MachO)) |macho_file| { const atom_index = macho_file.getAtomIndexForSymbol(.{ .sym_index = symbol.atom_index }).?; + const target = if (link.File.MachO.global_symbol_bit & symbol.sym_index != 0) + macho_file.getGlobalByIndex(link.File.MachO.global_symbol_mask & symbol.sym_index) + else + link.File.MachO.SymbolWithLoc{ .sym_index = symbol.sym_index }; try link.File.MachO.Atom.addRelocation(macho_file, atom_index, .{ .type = switch (lowered_relocs[0].target) { .linker_got => .got, @@ -123,7 +133,7 @@ pub fn emitMir(emit: *Emit) Error!void { .linker_tlv => .tlv, else => unreachable, }, - .target = .{ .sym_index = symbol.sym_index }, + .target = target, .offset = @as(u32, @intCast(end_offset - 4)), .addend = 0, .pcrel = true, @@ -134,6 +144,10 @@ pub fn emitMir(emit: *Emit) Error!void { .sym_index = symbol.atom_index, .file = null, }).?; + const target = if (link.File.Coff.global_symbol_bit & symbol.sym_index != 0) + coff_file.getGlobalByIndex(link.File.Coff.global_symbol_mask & symbol.sym_index) + else + link.File.Coff.SymbolWithLoc{ .sym_index = symbol.sym_index, .file = null }; try link.File.Coff.Atom.addRelocation(coff_file, atom_index, .{ .type = switch (lowered_relocs[0].target) { .linker_got => .got, @@ -141,13 +155,7 @@ pub fn emitMir(emit: *Emit) Error!void { .linker_import => .import, else => unreachable, }, - .target = switch (lowered_relocs[0].target) { - .linker_got, - .linker_direct, - => .{ .sym_index = symbol.sym_index, .file = null }, - .linker_import => coff_file.getGlobalByIndex(symbol.sym_index), - else => unreachable, - }, + .target = target, .offset = @as(u32, @intCast(end_offset - 4)), .addend = 0, .pcrel = true, diff --git a/src/codegen.zig b/src/codegen.zig index 4db32df16f..c46e41c6e6 100644 --- a/src/codegen.zig +++ b/src/codegen.zig @@ -721,6 +721,7 @@ fn lowerAnonDeclRef( const ptr_width_bytes = @divExact(target.ptrBitWidth(), 8); const decl_val = anon_decl.val; const decl_ty = mod.intern_pool.typeOf(decl_val).toType(); + log.debug("lowerAnonDecl: ty = {}", .{decl_ty.fmt(mod)}); const is_fn_body = decl_ty.zigTypeTag(mod) == .Fn; if (!is_fn_body and !decl_ty.hasRuntimeBits(mod)) { try code.appendNTimes(0xaa, ptr_width_bytes); @@ -911,6 +912,14 @@ fn genDeclRef( _ = try sym.getOrCreateZigGotEntry(sym_index, elf_file); return GenResult.mcv(.{ .load_symbol = sym.esym_index }); } else if (bin_file.cast(link.File.MachO)) |macho_file| { + if (is_extern) { + // TODO make this part of getGlobalSymbol + const name = mod.intern_pool.stringToSlice(decl.name); + const sym_name = try std.fmt.allocPrint(bin_file.allocator, "_{s}", .{name}); + defer bin_file.allocator.free(sym_name); + const global_index = try macho_file.addUndefined(sym_name, .{ .add_got = true }); + return GenResult.mcv(.{ .load_got = link.File.MachO.global_symbol_bit | global_index }); + } const atom_index = try macho_file.getOrCreateAtomForDecl(decl_index); const sym_index = macho_file.getAtom(atom_index).getSymbolIndex().?; if (is_threadlocal) { @@ -918,6 +927,17 @@ fn genDeclRef( } return GenResult.mcv(.{ .load_got = sym_index }); } else if (bin_file.cast(link.File.Coff)) |coff_file| { + if (is_extern) { + const name = mod.intern_pool.stringToSlice(decl.name); + // TODO audit this + const lib_name = if (decl.getOwnedVariable(mod)) |ov| + mod.intern_pool.stringToSliceUnwrap(ov.lib_name) + else + null; + const global_index = try coff_file.getGlobalSymbol(name, lib_name); + try coff_file.need_got_table.put(bin_file.allocator, global_index, {}); // needs GOT + return GenResult.mcv(.{ .load_got = link.File.Coff.global_symbol_bit | global_index }); + } const atom_index = try coff_file.getOrCreateAtomForDecl(decl_index); const sym_index = coff_file.getAtom(atom_index).getSymbolIndex().?; return GenResult.mcv(.{ .load_got = sym_index }); diff --git a/src/link/Coff.zig b/src/link/Coff.zig index 5d12e82478..340b60534d 100644 --- a/src/link/Coff.zig +++ b/src/link/Coff.zig @@ -28,6 +28,7 @@ locals: std.ArrayListUnmanaged(coff.Symbol) = .{}, globals: std.ArrayListUnmanaged(SymbolWithLoc) = .{}, resolver: std.StringHashMapUnmanaged(u32) = .{}, unresolved: std.AutoArrayHashMapUnmanaged(u32, bool) = .{}, +need_got_table: std.AutoHashMapUnmanaged(u32, void) = .{}, locals_free_list: std.ArrayListUnmanaged(u32) = .{}, globals_free_list: std.ArrayListUnmanaged(u32) = .{}, @@ -54,7 +55,7 @@ entry_addr: ?u32 = null, lazy_syms: LazySymbolTable = .{}, /// Table of tracked Decls. -decls: std.AutoArrayHashMapUnmanaged(Module.Decl.Index, DeclMetadata) = .{}, +decls: DeclTable = .{}, /// List of atoms that are either synthetic or map directly to the Zig source program. atoms: std.ArrayListUnmanaged(Atom) = .{}, @@ -108,7 +109,8 @@ const HotUpdateState = struct { loaded_base_address: ?std.os.windows.HMODULE = null, }; -const AnonDeclTable = std.AutoHashMapUnmanaged(InternPool.Index, Atom.Index); +const DeclTable = std.AutoArrayHashMapUnmanaged(Module.Decl.Index, DeclMetadata); +const AnonDeclTable = std.AutoHashMapUnmanaged(InternPool.Index, DeclMetadata); const RelocTable = std.AutoArrayHashMapUnmanaged(Atom.Index, std.ArrayListUnmanaged(Relocation)); const BaseRelocationTable = std.AutoArrayHashMapUnmanaged(Atom.Index, std.ArrayListUnmanaged(u32)); const UnnamedConstTable = std.AutoArrayHashMapUnmanaged(Module.Decl.Index, std.ArrayListUnmanaged(Atom.Index)); @@ -325,7 +327,14 @@ pub fn deinit(self: *Coff) void { atoms.deinit(gpa); } self.unnamed_const_atoms.deinit(gpa); - self.anon_decls.deinit(gpa); + + { + var it = self.anon_decls.iterator(); + while (it.next()) |entry| { + entry.value_ptr.exports.deinit(gpa); + } + self.anon_decls.deinit(gpa); + } for (self.relocs.values()) |*relocs| { relocs.deinit(gpa); @@ -1160,12 +1169,17 @@ pub fn updateDecl( const decl = mod.declPtr(decl_index); if (decl.val.getExternFunc(mod)) |_| { - return; // TODO Should we do more when front-end analyzed extern decl? + return; } - if (decl.val.getVariable(mod)) |variable| { - if (variable.is_extern) { - return; // TODO Should we do more when front-end analyzed extern decl? - } + + if (decl.isExtern(mod)) { + // TODO make this part of getGlobalSymbol + const variable = decl.getOwnedVariable(mod).?; + const name = mod.intern_pool.stringToSlice(decl.name); + const lib_name = mod.intern_pool.stringToSliceUnwrap(variable.lib_name); + const global_index = try self.getGlobalSymbol(name, lib_name); + try self.need_got_table.put(self.base.allocator, global_index, {}); + return; } const atom_index = try self.getOrCreateAtomForDecl(decl_index); @@ -1462,62 +1476,77 @@ pub fn updateExports( const gpa = self.base.allocator; - const decl_index = switch (exported) { - .decl_index => |i| i, - .value => |val| { - _ = val; - @panic("TODO: implement COFF linker code for exporting a constant value"); + const metadata = switch (exported) { + .decl_index => |decl_index| blk: { + _ = try self.getOrCreateAtomForDecl(decl_index); + break :blk self.decls.getPtr(decl_index).?; + }, + .value => |value| self.anon_decls.getPtr(value) orelse blk: { + const first_exp = exports[0]; + const res = try self.lowerAnonDecl(value, .none, first_exp.getSrcLoc(mod)); + switch (res) { + .ok => {}, + .fail => |em| { + // TODO maybe it's enough to return an error here and let Module.processExportsInner + // handle the error? + try mod.failed_exports.ensureUnusedCapacity(mod.gpa, 1); + mod.failed_exports.putAssumeCapacityNoClobber(first_exp, em); + return; + }, + } + break :blk self.anon_decls.getPtr(value).?; }, }; - const decl = mod.declPtr(decl_index); - const atom_index = try self.getOrCreateAtomForDecl(decl_index); + const atom_index = metadata.atom; const atom = self.getAtom(atom_index); - const decl_metadata = self.decls.getPtr(decl_index).?; for (exports) |exp| { log.debug("adding new export '{}'", .{exp.opts.name.fmt(&mod.intern_pool)}); if (mod.intern_pool.stringToSliceUnwrap(exp.opts.section)) |section_name| { if (!mem.eql(u8, section_name, ".text")) { - try mod.failed_exports.putNoClobber( + try mod.failed_exports.putNoClobber(gpa, exp, try Module.ErrorMsg.create( gpa, - exp, - try Module.ErrorMsg.create( - gpa, - decl.srcLoc(mod), - "Unimplemented: ExportOptions.section", - .{}, - ), - ); + exp.getSrcLoc(mod), + "Unimplemented: ExportOptions.section", + .{}, + )); continue; } } if (exp.opts.linkage == .LinkOnce) { - try mod.failed_exports.putNoClobber( + try mod.failed_exports.putNoClobber(gpa, exp, try Module.ErrorMsg.create( gpa, - exp, - try Module.ErrorMsg.create( - gpa, - decl.srcLoc(mod), - "Unimplemented: GlobalLinkage.LinkOnce", - .{}, - ), - ); + exp.getSrcLoc(mod), + "Unimplemented: GlobalLinkage.LinkOnce", + .{}, + )); continue; } - const sym_index = decl_metadata.getExport(self, mod.intern_pool.stringToSlice(exp.opts.name)) orelse blk: { - const sym_index = try self.allocateSymbol(); - try decl_metadata.exports.append(gpa, sym_index); + const exp_name = mod.intern_pool.stringToSlice(exp.opts.name); + const sym_index = metadata.getExport(self, exp_name) orelse blk: { + const sym_index = if (self.getGlobalIndex(exp_name)) |global_index| ind: { + const global = self.globals.items[global_index]; + // TODO this is just plain wrong as it all should happen in a single `resolveSymbols` + // pass. This will go away once we abstact away Zig's incremental compilation into + // its own module. + if (global.file == null and self.getSymbol(global).section_number == .UNDEFINED) { + _ = self.unresolved.swapRemove(global_index); + break :ind global.sym_index; + } + break :ind try self.allocateSymbol(); + } else try self.allocateSymbol(); + try metadata.exports.append(gpa, sym_index); break :blk sym_index; }; const sym_loc = SymbolWithLoc{ .sym_index = sym_index, .file = null }; const sym = self.getSymbolPtr(sym_loc); - try self.setSymbolName(sym, mod.intern_pool.stringToSlice(exp.opts.name)); + try self.setSymbolName(sym, exp_name); sym.value = atom.getSymbol(self).value; - sym.section_number = @as(coff.SectionNumber, @enumFromInt(self.text_section_index.? + 1)); - sym.type = .{ .complex_type = .FUNCTION, .base_type = .NULL }; + sym.section_number = @as(coff.SectionNumber, @enumFromInt(metadata.section + 1)); + sym.type = atom.getSymbol(self).type; switch (exp.opts.linkage) { .Strong => { @@ -1651,8 +1680,16 @@ pub fn flushModule(self: *Coff, comp: *Compilation, prog_node: *std.Progress.Nod if (metadata.rdata_state != .unused) metadata.rdata_state = .flushed; } + { + var it = self.need_got_table.iterator(); + while (it.next()) |entry| { + const global = self.globals.items[entry.key_ptr.*]; + try self.addGotEntry(global); + } + } + while (self.unresolved.popOrNull()) |entry| { - assert(entry.value); // We only expect imports generated by the incremental linker for now. + assert(entry.value); const global = self.globals.items[entry.key]; const sym = self.getSymbol(global); const res = try self.import_tables.getOrPut(gpa, sym.value); @@ -1761,8 +1798,8 @@ pub fn lowerAnonDecl( .none => ty.abiAlignment(mod), else => explicit_alignment, }; - if (self.anon_decls.get(decl_val)) |atom_index| { - const existing_addr = self.getAtom(atom_index).getSymbol(self).value; + if (self.anon_decls.get(decl_val)) |metadata| { + const existing_addr = self.getAtom(metadata.atom).getSymbol(self).value; if (decl_alignment.check(existing_addr)) return .ok; } @@ -1792,14 +1829,14 @@ pub fn lowerAnonDecl( .ok => |atom_index| atom_index, .fail => |em| return .{ .fail = em }, }; - try self.anon_decls.put(gpa, decl_val, atom_index); + try self.anon_decls.put(gpa, decl_val, .{ .atom = atom_index, .section = self.rdata_section_index.? }); return .ok; } pub fn getAnonDeclVAddr(self: *Coff, decl_val: InternPool.Index, reloc_info: link.File.RelocInfo) !u64 { assert(self.llvm_object == null); - const this_atom_index = self.anon_decls.get(decl_val).?; + const this_atom_index = self.anon_decls.get(decl_val).?.atom; const sym_index = self.getAtom(this_atom_index).getSymbolIndex().?; const atom_index = self.getAtomIndexForSymbol(.{ .sym_index = reloc_info.parent_atom_index, .file = null }).?; const target = SymbolWithLoc{ .sym_index = sym_index, .file = null }; @@ -2447,6 +2484,11 @@ const GetOrPutGlobalPtrResult = struct { value_ptr: *SymbolWithLoc, }; +/// Used only for disambiguating local from global at relocation level. +/// TODO this must go away. +pub const global_symbol_bit: u32 = 0x80000000; +pub const global_symbol_mask: u32 = 0x7fffffff; + /// Return pointer to the global entry for `name` if one exists. /// Puts a new global entry for `name` if one doesn't exist, and /// returns a pointer to it. diff --git a/src/link/Elf.zig b/src/link/Elf.zig index f5d6a78298..15a10f1b63 100644 --- a/src/link/Elf.zig +++ b/src/link/Elf.zig @@ -185,12 +185,12 @@ misc_errors: std.ArrayListUnmanaged(link.File.ErrorMsg) = .{}, lazy_syms: LazySymbolTable = .{}, /// Table of tracked Decls. -decls: std.AutoHashMapUnmanaged(Module.Decl.Index, DeclMetadata) = .{}, +decls: DeclTable = .{}, /// List of atoms that are owned directly by the linker. atoms: std.ArrayListUnmanaged(Atom) = .{}, /// Table of last atom index in a section and matching atom free list if any. -last_atom_and_free_list_table: std.AutoArrayHashMapUnmanaged(u16, LastAtomAndFreeList) = .{}, +last_atom_and_free_list_table: LastAtomAndFreeListTable = .{}, /// Table of unnamed constants associated with a parent `Decl`. /// We store them here so that we can free the constants whenever the `Decl` @@ -220,8 +220,10 @@ comdat_groups_table: std.AutoHashMapUnmanaged(u32, ComdatGroupOwner.Index) = .{} const AtomList = std.ArrayListUnmanaged(Atom.Index); const UnnamedConstTable = std.AutoHashMapUnmanaged(Module.Decl.Index, std.ArrayListUnmanaged(Symbol.Index)); -const AnonDeclTable = std.AutoHashMapUnmanaged(InternPool.Index, Symbol.Index); +const DeclTable = std.AutoHashMapUnmanaged(Module.Decl.Index, DeclMetadata); +const AnonDeclTable = std.AutoHashMapUnmanaged(InternPool.Index, DeclMetadata); const LazySymbolTable = std.AutoArrayHashMapUnmanaged(Module.Decl.OptionalIndex, LazySymbolMetadata); +const LastAtomAndFreeListTable = std.AutoArrayHashMapUnmanaged(u16, LastAtomAndFreeList); /// When allocating, the ideal_capacity is calculated by /// actual_capacity + (actual_capacity / ideal_factor) @@ -445,7 +447,14 @@ pub fn deinit(self: *Elf) void { } self.unnamed_consts.deinit(gpa); } - self.anon_decls.deinit(gpa); + + { + var it = self.anon_decls.iterator(); + while (it.next()) |entry| { + entry.value_ptr.exports.deinit(gpa); + } + self.anon_decls.deinit(gpa); + } if (self.dwarf) |*dw| { dw.deinit(); @@ -497,8 +506,8 @@ pub fn lowerAnonDecl( .none => ty.abiAlignment(mod), else => explicit_alignment, }; - if (self.anon_decls.get(decl_val)) |sym_index| { - const existing_alignment = self.symbol(sym_index).atom(self).?.alignment; + if (self.anon_decls.get(decl_val)) |metadata| { + const existing_alignment = self.symbol(metadata.symbol_index).atom(self).?.alignment; if (decl_alignment.order(existing_alignment).compare(.lte)) return .ok; } @@ -528,13 +537,13 @@ pub fn lowerAnonDecl( .ok => |sym_index| sym_index, .fail => |em| return .{ .fail = em }, }; - try self.anon_decls.put(gpa, decl_val, sym_index); + try self.anon_decls.put(gpa, decl_val, .{ .symbol_index = sym_index }); return .ok; } pub fn getAnonDeclVAddr(self: *Elf, decl_val: InternPool.Index, reloc_info: link.File.RelocInfo) !u64 { assert(self.llvm_object == null); - const sym_index = self.anon_decls.get(decl_val).?; + const sym_index = self.anon_decls.get(decl_val).?.symbol_index; const sym = self.symbol(sym_index); const vaddr = sym.value; const parent_atom = self.symbol(reloc_info.parent_atom_index).atom(self).?; @@ -3122,10 +3131,7 @@ pub fn getOrCreateMetadataForDecl(self: *Elf, decl_index: Module.Decl.Index) !Sy const gop = try self.decls.getOrPut(self.base.allocator, decl_index); if (!gop.found_existing) { const zig_module = self.file(self.zig_module_index.?).?.zig_module; - gop.value_ptr.* = .{ - .symbol_index = try zig_module.addAtom(self), - .exports = .{}, - }; + gop.value_ptr.* = .{ .symbol_index = try zig_module.addAtom(self) }; } return gop.value_ptr.symbol_index; } @@ -3573,31 +3579,43 @@ pub fn updateExports( defer tracy.end(); const gpa = self.base.allocator; - - const decl_index = switch (exported) { - .decl_index => |i| i, - .value => |val| { - _ = val; - @panic("TODO: implement ELF linker code for exporting a constant value"); + const zig_module = self.file(self.zig_module_index.?).?.zig_module; + const metadata = switch (exported) { + .decl_index => |decl_index| blk: { + _ = try self.getOrCreateMetadataForDecl(decl_index); + break :blk self.decls.getPtr(decl_index).?; + }, + .value => |value| self.anon_decls.getPtr(value) orelse blk: { + const first_exp = exports[0]; + const res = try self.lowerAnonDecl(value, .none, first_exp.getSrcLoc(mod)); + switch (res) { + .ok => {}, + .fail => |em| { + // TODO maybe it's enough to return an error here and let Module.processExportsInner + // handle the error? + try mod.failed_exports.ensureUnusedCapacity(mod.gpa, 1); + mod.failed_exports.putAssumeCapacityNoClobber(first_exp, em); + return; + }, + } + break :blk self.anon_decls.getPtr(value).?; }, }; - const zig_module = self.file(self.zig_module_index.?).?.zig_module; - const decl = mod.declPtr(decl_index); - const decl_sym_index = try self.getOrCreateMetadataForDecl(decl_index); - const decl_esym_index = self.symbol(decl_sym_index).esym_index; - const decl_esym = zig_module.local_esyms.items(.elf_sym)[decl_esym_index]; - const decl_esym_shndx = zig_module.local_esyms.items(.shndx)[decl_esym_index]; - const decl_metadata = self.decls.getPtr(decl_index).?; + const sym_index = metadata.symbol_index; + const esym_index = self.symbol(sym_index).esym_index; + const esym = zig_module.local_esyms.items(.elf_sym)[esym_index]; + const esym_shndx = zig_module.local_esyms.items(.shndx)[esym_index]; for (exports) |exp| { - const exp_name = mod.intern_pool.stringToSlice(exp.opts.name); if (exp.opts.section.unwrap()) |section_name| { if (!mod.intern_pool.stringEqlSlice(section_name, ".text")) { try mod.failed_exports.ensureUnusedCapacity(mod.gpa, 1); - mod.failed_exports.putAssumeCapacityNoClobber( - exp, - try Module.ErrorMsg.create(gpa, decl.srcLoc(mod), "Unimplemented: ExportOptions.section", .{}), - ); + mod.failed_exports.putAssumeCapacityNoClobber(exp, try Module.ErrorMsg.create( + gpa, + exp.getSrcLoc(mod), + "Unimplemented: ExportOptions.section", + .{}, + )); continue; } } @@ -3607,34 +3625,37 @@ pub fn updateExports( .Weak => elf.STB_WEAK, .LinkOnce => { try mod.failed_exports.ensureUnusedCapacity(mod.gpa, 1); - mod.failed_exports.putAssumeCapacityNoClobber( - exp, - try Module.ErrorMsg.create(gpa, decl.srcLoc(mod), "Unimplemented: GlobalLinkage.LinkOnce", .{}), - ); + mod.failed_exports.putAssumeCapacityNoClobber(exp, try Module.ErrorMsg.create( + gpa, + exp.getSrcLoc(mod), + "Unimplemented: GlobalLinkage.LinkOnce", + .{}, + )); continue; }, }; - const stt_bits: u8 = @as(u4, @truncate(decl_esym.st_info)); - + const stt_bits: u8 = @as(u4, @truncate(esym.st_info)); + const exp_name = mod.intern_pool.stringToSlice(exp.opts.name); const name_off = try self.strtab.insert(gpa, exp_name); - const sym_index = if (decl_metadata.@"export"(self, exp_name)) |exp_index| exp_index.* else blk: { - const sym_index = try zig_module.addGlobalEsym(gpa); + const global_esym_index = if (metadata.@"export"(self, exp_name)) |exp_index| exp_index.* else blk: { + const global_esym_index = try zig_module.addGlobalEsym(gpa); const lookup_gop = try zig_module.globals_lookup.getOrPut(gpa, name_off); - const esym = zig_module.elfSym(sym_index); - esym.st_name = name_off; - lookup_gop.value_ptr.* = sym_index; - try decl_metadata.exports.append(gpa, sym_index); + const global_esym = zig_module.elfSym(global_esym_index); + global_esym.st_name = name_off; + lookup_gop.value_ptr.* = global_esym_index; + try metadata.exports.append(gpa, global_esym_index); const gop = try self.getOrPutGlobal(name_off); try zig_module.global_symbols.append(gpa, gop.index); - break :blk sym_index; + break :blk global_esym_index; }; - const global_esym_index = sym_index & ZigModule.symbol_mask; - const global_esym = &zig_module.global_esyms.items(.elf_sym)[global_esym_index]; - global_esym.st_value = self.symbol(decl_sym_index).value; - global_esym.st_shndx = decl_esym.st_shndx; + + const actual_esym_index = global_esym_index & ZigModule.symbol_mask; + const global_esym = &zig_module.global_esyms.items(.elf_sym)[actual_esym_index]; + global_esym.st_value = self.symbol(sym_index).value; + global_esym.st_shndx = esym.st_shndx; global_esym.st_info = (stb_bits << 4) | stt_bits; global_esym.st_name = name_off; - zig_module.global_esyms.items(.shndx)[global_esym_index] = decl_esym_shndx; + zig_module.global_esyms.items(.shndx)[actual_esym_index] = esym_shndx; } } diff --git a/src/link/MachO.zig b/src/link/MachO.zig index b88911b6ce..f3f9f5b4b8 100644 --- a/src/link/MachO.zig +++ b/src/link/MachO.zig @@ -50,7 +50,7 @@ tlv_ptr_section_index: ?u8 = null, locals: std.ArrayListUnmanaged(macho.nlist_64) = .{}, globals: std.ArrayListUnmanaged(SymbolWithLoc) = .{}, resolver: std.StringHashMapUnmanaged(u32) = .{}, -unresolved: std.AutoArrayHashMapUnmanaged(u32, ResolveAction.Kind) = .{}, +unresolved: std.AutoArrayHashMapUnmanaged(u32, void) = .{}, locals_free_list: std.ArrayListUnmanaged(u32) = .{}, globals_free_list: std.ArrayListUnmanaged(u32) = .{}, @@ -115,6 +115,10 @@ anon_decls: AnonDeclTable = .{}, /// Note that once we refactor `Atom`'s lifetime and ownership rules, /// this will be a table indexed by index into the list of Atoms. relocs: RelocationTable = .{}, +/// TODO I do not have time to make this right but this will go once +/// MachO linker is rewritten more-or-less to feature the same resolution +/// mechanism as the ELF linker. +actions: ActionTable = .{}, /// A table of rebases indexed by the owning them `Atom`. /// Note that once we refactor `Atom`'s lifetime and ownership rules, @@ -130,7 +134,7 @@ bindings: BindingTable = .{}, lazy_syms: LazySymbolTable = .{}, /// Table of tracked Decls. -decls: std.AutoArrayHashMapUnmanaged(Module.Decl.Index, DeclMetadata) = .{}, +decls: DeclTable = .{}, /// Table of threadlocal variables descriptors. /// They are emitted in the `__thread_vars` section. @@ -417,9 +421,7 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No try self.parseDependentLibs(&dependent_libs); } - var actions = std.ArrayList(ResolveAction).init(self.base.allocator); - defer actions.deinit(); - try self.resolveSymbols(&actions); + try self.resolveSymbols(); if (self.getEntryPoint() == null) { self.error_flags.no_entry_point_found = true; @@ -429,11 +431,16 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No return error.FlushFailure; } - for (actions.items) |action| switch (action.kind) { - .none => {}, - .add_got => try self.addGotEntry(action.target), - .add_stub => try self.addStubEntry(action.target), - }; + { + var it = self.actions.iterator(); + while (it.next()) |entry| { + const global_index = entry.key_ptr.*; + const global = self.globals.items[global_index]; + const flags = entry.value_ptr.*; + if (flags.add_got) try self.addGotEntry(global); + if (flags.add_stub) try self.addStubEntry(global); + } + } try self.createDyldPrivateAtom(); try self.writeStubHelperPreamble(); @@ -1589,18 +1596,18 @@ pub fn createDsoHandleSymbol(self: *MachO) !void { _ = self.unresolved.swapRemove(self.getGlobalIndex("___dso_handle").?); } -pub fn resolveSymbols(self: *MachO, actions: *std.ArrayList(ResolveAction)) !void { +pub fn resolveSymbols(self: *MachO) !void { // We add the specified entrypoint as the first unresolved symbols so that // we search for it in libraries should there be no object files specified // on the linker line. if (self.base.options.output_mode == .Exe) { const entry_name = self.base.options.entry orelse load_commands.default_entry_point; - _ = try self.addUndefined(entry_name, .none); + _ = try self.addUndefined(entry_name, .{}); } // Force resolution of any symbols requested by the user. for (self.base.options.force_undefined_symbols.keys()) |sym_name| { - _ = try self.addUndefined(sym_name, .none); + _ = try self.addUndefined(sym_name, .{}); } for (self.objects.items, 0..) |_, object_id| { @@ -1612,13 +1619,13 @@ pub fn resolveSymbols(self: *MachO, actions: *std.ArrayList(ResolveAction)) !voi // Finally, force resolution of dyld_stub_binder if there are imports // requested. if (self.unresolved.count() > 0 and self.dyld_stub_binder_index == null) { - self.dyld_stub_binder_index = try self.addUndefined("dyld_stub_binder", .add_got); + self.dyld_stub_binder_index = try self.addUndefined("dyld_stub_binder", .{ .add_got = true }); } if (!self.base.options.single_threaded and self.mode == .incremental) { - _ = try self.addUndefined("__tlv_bootstrap", .none); + _ = try self.addUndefined("__tlv_bootstrap", .{}); } - try self.resolveSymbolsInDylibs(actions); + try self.resolveSymbolsInDylibs(); try self.createMhExecuteHeaderSymbol(); try self.createDsoHandleSymbol(); @@ -1634,7 +1641,7 @@ fn resolveGlobalSymbol(self: *MachO, current: SymbolWithLoc) !void { if (!gop.found_existing) { gop.value_ptr.* = current; if (sym.undf() and !sym.tentative()) { - try self.unresolved.putNoClobber(gpa, self.getGlobalIndex(sym_name).?, .none); + try self.unresolved.putNoClobber(gpa, self.getGlobalIndex(sym_name).?, {}); } return; } @@ -1766,7 +1773,7 @@ fn resolveSymbolsInArchives(self: *MachO) !void { } } -fn resolveSymbolsInDylibs(self: *MachO, actions: *std.ArrayList(ResolveAction)) !void { +fn resolveSymbolsInDylibs(self: *MachO) !void { if (self.dylibs.items.len == 0) return; const gpa = self.base.allocator; @@ -1793,11 +1800,7 @@ fn resolveSymbolsInDylibs(self: *MachO, actions: *std.ArrayList(ResolveAction)) sym.n_desc |= macho.N_WEAK_REF; } - if (self.unresolved.fetchSwapRemove(global_index)) |entry| blk: { - if (!sym.undf()) break :blk; - if (self.mode == .zld) break :blk; - try actions.append(.{ .kind = entry.value, .target = global }); - } + _ = self.unresolved.swapRemove(global_index); continue :loop; } @@ -1904,6 +1907,7 @@ pub fn deinit(self: *MachO) void { m.exports.deinit(gpa); } self.decls.deinit(gpa); + self.lazy_syms.deinit(gpa); self.tlv_table.deinit(gpa); @@ -1911,7 +1915,14 @@ pub fn deinit(self: *MachO) void { atoms.deinit(gpa); } self.unnamed_const_atoms.deinit(gpa); - self.anon_decls.deinit(gpa); + + { + var it = self.anon_decls.iterator(); + while (it.next()) |entry| { + entry.value_ptr.exports.deinit(gpa); + } + self.anon_decls.deinit(gpa); + } self.atom_by_index_table.deinit(gpa); @@ -1919,6 +1930,7 @@ pub fn deinit(self: *MachO) void { relocs.deinit(gpa); } self.relocs.deinit(gpa); + self.actions.deinit(gpa); for (self.rebases.values()) |*rebases| { rebases.deinit(gpa); @@ -2258,6 +2270,7 @@ fn lowerConst( log.debug(" (required alignment 0x{x})", .{required_alignment}); try self.writeAtom(atom_index, code); + self.markRelocsDirtyByTarget(atom.getSymbolWithLoc()); return .{ .ok = atom_index }; } @@ -2273,12 +2286,16 @@ pub fn updateDecl(self: *MachO, mod: *Module, decl_index: Module.Decl.Index) !vo const decl = mod.declPtr(decl_index); if (decl.val.getExternFunc(mod)) |_| { - return; // TODO Should we do more when front-end analyzed extern decl? + return; } - if (decl.val.getVariable(mod)) |variable| { - if (variable.is_extern) { - return; // TODO Should we do more when front-end analyzed extern decl? - } + + if (decl.isExtern(mod)) { + // TODO make this part of getGlobalSymbol + const name = mod.intern_pool.stringToSlice(decl.name); + const sym_name = try std.fmt.allocPrint(self.base.allocator, "_{s}", .{name}); + defer self.base.allocator.free(sym_name); + _ = try self.addUndefined(sym_name, .{ .add_got = true }); + return; } const is_threadlocal = if (decl.val.getVariable(mod)) |variable| @@ -2689,18 +2706,30 @@ pub fn updateExports( const gpa = self.base.allocator; - const decl_index = switch (exported) { - .decl_index => |i| i, - .value => |val| { - _ = val; - @panic("TODO: implement MachO linker code for exporting a constant value"); + const metadata = switch (exported) { + .decl_index => |decl_index| blk: { + _ = try self.getOrCreateAtomForDecl(decl_index); + break :blk self.decls.getPtr(decl_index).?; + }, + .value => |value| self.anon_decls.getPtr(value) orelse blk: { + const first_exp = exports[0]; + const res = try self.lowerAnonDecl(value, .none, first_exp.getSrcLoc(mod)); + switch (res) { + .ok => {}, + .fail => |em| { + // TODO maybe it's enough to return an error here and let Module.processExportsInner + // handle the error? + try mod.failed_exports.ensureUnusedCapacity(mod.gpa, 1); + mod.failed_exports.putAssumeCapacityNoClobber(first_exp, em); + return; + }, + } + break :blk self.anon_decls.getPtr(value).?; }, }; - const decl = mod.declPtr(decl_index); - const atom_index = try self.getOrCreateAtomForDecl(decl_index); + const atom_index = metadata.atom; const atom = self.getAtom(atom_index); - const decl_sym = atom.getSymbol(self); - const decl_metadata = self.decls.getPtr(decl_index).?; + const sym = atom.getSymbol(self); for (exports) |exp| { const exp_name = try std.fmt.allocPrint(gpa, "_{}", .{ @@ -2712,73 +2741,75 @@ pub fn updateExports( if (exp.opts.section.unwrap()) |section_name| { if (!mod.intern_pool.stringEqlSlice(section_name, "__text")) { - try mod.failed_exports.putNoClobber( - mod.gpa, - exp, - try Module.ErrorMsg.create( - gpa, - decl.srcLoc(mod), - "Unimplemented: ExportOptions.section", - .{}, - ), - ); + try mod.failed_exports.putNoClobber(mod.gpa, exp, try Module.ErrorMsg.create( + gpa, + exp.getSrcLoc(mod), + "Unimplemented: ExportOptions.section", + .{}, + )); continue; } } if (exp.opts.linkage == .LinkOnce) { - try mod.failed_exports.putNoClobber( - mod.gpa, - exp, - try Module.ErrorMsg.create( - gpa, - decl.srcLoc(mod), - "Unimplemented: GlobalLinkage.LinkOnce", - .{}, - ), - ); + try mod.failed_exports.putNoClobber(mod.gpa, exp, try Module.ErrorMsg.create( + gpa, + exp.getSrcLoc(mod), + "Unimplemented: GlobalLinkage.LinkOnce", + .{}, + )); continue; } - const sym_index = decl_metadata.getExport(self, exp_name) orelse blk: { - const sym_index = try self.allocateSymbol(); - try decl_metadata.exports.append(gpa, sym_index); - break :blk sym_index; + const global_sym_index = metadata.getExport(self, exp_name) orelse blk: { + const global_sym_index = if (self.getGlobalIndex(exp_name)) |global_index| ind: { + const global = self.globals.items[global_index]; + // TODO this is just plain wrong as it all should happen in a single `resolveSymbols` + // pass. This will go away once we abstact away Zig's incremental compilation into + // its own module. + if (global.getFile() == null and self.getSymbol(global).undf()) { + _ = self.unresolved.swapRemove(global_index); + break :ind global.sym_index; + } + break :ind try self.allocateSymbol(); + } else try self.allocateSymbol(); + try metadata.exports.append(gpa, global_sym_index); + break :blk global_sym_index; }; - const sym_loc = SymbolWithLoc{ .sym_index = sym_index }; - const sym = self.getSymbolPtr(sym_loc); - sym.* = .{ + const global_sym_loc = SymbolWithLoc{ .sym_index = global_sym_index }; + const global_sym = self.getSymbolPtr(global_sym_loc); + global_sym.* = .{ .n_strx = try self.strtab.insert(gpa, exp_name), .n_type = macho.N_SECT | macho.N_EXT, - .n_sect = self.text_section_index.? + 1, // TODO what if we export a variable? + .n_sect = metadata.section + 1, .n_desc = 0, - .n_value = decl_sym.n_value, + .n_value = sym.n_value, }; switch (exp.opts.linkage) { .Internal => { // Symbol should be hidden, or in MachO lingo, private extern. // We should also mark the symbol as Weak: n_desc == N_WEAK_DEF. - sym.n_type |= macho.N_PEXT; - sym.n_desc |= macho.N_WEAK_DEF; + global_sym.n_type |= macho.N_PEXT; + global_sym.n_desc |= macho.N_WEAK_DEF; }, .Strong => {}, .Weak => { // Weak linkage is specified as part of n_desc field. // Symbol's n_type is like for a symbol with strong linkage. - sym.n_desc |= macho.N_WEAK_DEF; + global_sym.n_desc |= macho.N_WEAK_DEF; }, else => unreachable, } - self.resolveGlobalSymbol(sym_loc) catch |err| switch (err) { + self.resolveGlobalSymbol(global_sym_loc) catch |err| switch (err) { error.MultipleSymbolDefinitions => { // TODO: this needs rethinking const global = self.getGlobal(exp_name).?; - if (sym_loc.sym_index != global.sym_index and global.getFile() != null) { + if (global_sym_loc.sym_index != global.sym_index and global.getFile() != null) { _ = try mod.failed_exports.put(mod.gpa, exp, try Module.ErrorMsg.create( gpa, - decl.srcLoc(mod), + exp.getSrcLoc(mod), \\LinkError: symbol '{s}' defined multiple times , .{exp_name}, @@ -2886,8 +2917,8 @@ pub fn lowerAnonDecl( .none => ty.abiAlignment(mod), else => explicit_alignment, }; - if (self.anon_decls.get(decl_val)) |atom_index| { - const existing_addr = self.getAtom(atom_index).getSymbol(self).n_value; + if (self.anon_decls.get(decl_val)) |metadata| { + const existing_addr = self.getAtom(metadata.atom).getSymbol(self).n_value; if (decl_alignment.check(existing_addr)) return .ok; } @@ -2917,14 +2948,17 @@ pub fn lowerAnonDecl( .ok => |atom_index| atom_index, .fail => |em| return .{ .fail = em }, }; - try self.anon_decls.put(gpa, decl_val, atom_index); + try self.anon_decls.put(gpa, decl_val, .{ + .atom = atom_index, + .section = self.data_const_section_index.?, + }); return .ok; } pub fn getAnonDeclVAddr(self: *MachO, decl_val: InternPool.Index, reloc_info: link.File.RelocInfo) !u64 { assert(self.llvm_object == null); - const this_atom_index = self.anon_decls.get(decl_val).?; + const this_atom_index = self.anon_decls.get(decl_val).?.atom; const sym_index = self.getAtom(this_atom_index).getSymbolIndex().?; const atom_index = self.getAtomIndexForSymbol(.{ .sym_index = reloc_info.parent_atom_index }).?; try Atom.addRelocation(self, atom_index, .{ @@ -3407,7 +3441,7 @@ pub fn getGlobalSymbol(self: *MachO, name: []const u8, lib_name: ?[]const u8) !u const gpa = self.base.allocator; const sym_name = try std.fmt.allocPrint(gpa, "_{s}", .{name}); defer gpa.free(sym_name); - return self.addUndefined(sym_name, .add_stub); + return self.addUndefined(sym_name, .{ .add_stub = true }); } pub fn writeSegmentHeaders(self: *MachO, writer: anytype) !void { @@ -4691,13 +4725,16 @@ pub fn ptraceDetach(self: *MachO, pid: std.os.pid_t) !void { self.hot_state.mach_task = null; } -fn addUndefined(self: *MachO, name: []const u8, action: ResolveAction.Kind) !u32 { +pub fn addUndefined(self: *MachO, name: []const u8, flags: RelocFlags) !u32 { const gpa = self.base.allocator; const gop = try self.getOrPutGlobalPtr(name); const global_index = self.getGlobalIndex(name).?; - if (gop.found_existing) return global_index; + if (gop.found_existing) { + try self.updateRelocActions(global_index, flags); + return global_index; + } const sym_index = try self.allocateSymbol(); const sym_loc = SymbolWithLoc{ .sym_index = sym_index }; @@ -4705,13 +4742,23 @@ fn addUndefined(self: *MachO, name: []const u8, action: ResolveAction.Kind) !u32 const sym = self.getSymbolPtr(sym_loc); sym.n_strx = try self.strtab.insert(gpa, name); - sym.n_type = macho.N_UNDF; + sym.n_type = macho.N_EXT | macho.N_UNDF; - try self.unresolved.putNoClobber(gpa, global_index, action); + try self.unresolved.putNoClobber(gpa, global_index, {}); + try self.updateRelocActions(global_index, flags); return global_index; } +fn updateRelocActions(self: *MachO, global_index: u32, flags: RelocFlags) !void { + const act_gop = try self.actions.getOrPut(self.base.allocator, global_index); + if (!act_gop.found_existing) { + act_gop.value_ptr.* = .{}; + } + act_gop.value_ptr.add_got = act_gop.value_ptr.add_got or flags.add_got; + act_gop.value_ptr.add_stub = act_gop.value_ptr.add_stub or flags.add_stub; +} + pub fn makeStaticString(bytes: []const u8) [16]u8 { var buf = [_]u8{0} ** 16; @memcpy(buf[0..bytes.len], bytes); @@ -4823,6 +4870,11 @@ const GetOrPutGlobalPtrResult = struct { value_ptr: *SymbolWithLoc, }; +/// Used only for disambiguating local from global at relocation level. +/// TODO this must go away. +pub const global_symbol_bit: u32 = 0x80000000; +pub const global_symbol_mask: u32 = 0x7fffffff; + /// Return pointer to the global entry for `name` if one exists. /// Puts a new global entry for `name` if one doesn't exist, and /// returns a pointer to it. @@ -5489,21 +5541,17 @@ const DeclMetadata = struct { } }; -const AnonDeclTable = std.AutoHashMapUnmanaged(InternPool.Index, Atom.Index); +const DeclTable = std.AutoArrayHashMapUnmanaged(Module.Decl.Index, DeclMetadata); +const AnonDeclTable = std.AutoHashMapUnmanaged(InternPool.Index, DeclMetadata); const BindingTable = std.AutoArrayHashMapUnmanaged(Atom.Index, std.ArrayListUnmanaged(Atom.Binding)); const UnnamedConstTable = std.AutoArrayHashMapUnmanaged(Module.Decl.Index, std.ArrayListUnmanaged(Atom.Index)); const RebaseTable = std.AutoArrayHashMapUnmanaged(Atom.Index, std.ArrayListUnmanaged(u32)); const RelocationTable = std.AutoArrayHashMapUnmanaged(Atom.Index, std.ArrayListUnmanaged(Relocation)); +const ActionTable = std.AutoHashMapUnmanaged(u32, RelocFlags); -pub const ResolveAction = struct { - kind: Kind, - target: SymbolWithLoc, - - const Kind = enum { - none, - add_got, - add_stub, - }; +pub const RelocFlags = packed struct { + add_got: bool = false, + add_stub: bool = false, }; pub const SymbolWithLoc = extern struct { diff --git a/src/link/MachO/Atom.zig b/src/link/MachO/Atom.zig index a548c4e538..25fc0aee24 100644 --- a/src/link/MachO/Atom.zig +++ b/src/link/MachO/Atom.zig @@ -300,7 +300,7 @@ pub fn resolveRelocations( relocs: []*const Relocation, code: []u8, ) void { - log.debug("relocating '{s}'", .{macho_file.getAtom(atom_index).getName(macho_file)}); + relocs_log.debug("relocating '{s}'", .{macho_file.getAtom(atom_index).getName(macho_file)}); for (relocs) |reloc| { reloc.resolve(macho_file, atom_index, code); } @@ -603,7 +603,7 @@ pub fn resolveRelocs( const atom = macho_file.getAtom(atom_index); assert(atom.getFile() != null); // synthetic atoms do not have relocs - log.debug("resolving relocations in ATOM(%{d}, '{s}')", .{ + relocs_log.debug("resolving relocations in ATOM(%{d}, '{s}')", .{ atom.sym_index, macho_file.getSymbolName(atom.getSymbolWithLoc()), }); @@ -683,7 +683,7 @@ fn resolveRelocsArm64( .ARM64_RELOC_ADDEND => { assert(addend == null); - log.debug(" RELA({s}) @ {x} => {x}", .{ @tagName(rel_type), rel.r_address, rel.r_symbolnum }); + relocs_log.debug(" RELA({s}) @ {x} => {x}", .{ @tagName(rel_type), rel.r_address, rel.r_symbolnum }); addend = rel.r_symbolnum; continue; @@ -691,7 +691,7 @@ fn resolveRelocsArm64( .ARM64_RELOC_SUBTRACTOR => { assert(subtractor == null); - log.debug(" RELA({s}) @ {x} => %{d} in object({?d})", .{ + relocs_log.debug(" RELA({s}) @ {x} => %{d} in object({?d})", .{ @tagName(rel_type), rel.r_address, rel.r_symbolnum, @@ -719,7 +719,7 @@ fn resolveRelocsArm64( }); const rel_offset = @as(u32, @intCast(rel.r_address - context.base_offset)); - log.debug(" RELA({s}) @ {x} => %{d} ('{s}') in object({?})", .{ + relocs_log.debug(" RELA({s}) @ {x} => %{d} ('{s}') in object({?})", .{ @tagName(rel_type), rel.r_address, target.sym_index, @@ -745,11 +745,11 @@ fn resolveRelocsArm64( break :blk getRelocTargetAddress(macho_file, target, is_tlv); }; - log.debug(" | source_addr = 0x{x}", .{source_addr}); + relocs_log.debug(" | source_addr = 0x{x}", .{source_addr}); switch (rel_type) { .ARM64_RELOC_BRANCH26 => { - log.debug(" source {s} (object({?})), target {s}", .{ + relocs_log.debug(" source {s} (object({?})), target {s}", .{ macho_file.getSymbolName(atom.getSymbolWithLoc()), atom.getFile(), macho_file.getSymbolName(target), @@ -759,7 +759,7 @@ fn resolveRelocsArm64( source_addr, target_addr, )) |disp| blk: { - log.debug(" | target_addr = 0x{x}", .{target_addr}); + relocs_log.debug(" | target_addr = 0x{x}", .{target_addr}); break :blk disp; } else |_| blk: { const thunk_index = macho_file.thunk_table.get(atom_index).?; @@ -769,7 +769,7 @@ fn resolveRelocsArm64( else thunk.getTrampoline(macho_file, .atom, target).?; const thunk_addr = macho_file.getSymbol(thunk_sym_loc).n_value; - log.debug(" | target_addr = 0x{x} (thunk)", .{thunk_addr}); + relocs_log.debug(" | target_addr = 0x{x} (thunk)", .{thunk_addr}); break :blk try Relocation.calcPcRelativeDisplacementArm64(source_addr, thunk_addr); }; @@ -790,7 +790,7 @@ fn resolveRelocsArm64( => { const adjusted_target_addr = @as(u64, @intCast(@as(i64, @intCast(target_addr)) + (addend orelse 0))); - log.debug(" | target_addr = 0x{x}", .{adjusted_target_addr}); + relocs_log.debug(" | target_addr = 0x{x}", .{adjusted_target_addr}); const pages = @as(u21, @bitCast(Relocation.calcNumberOfPages(source_addr, adjusted_target_addr))); const code = atom_code[rel_offset..][0..4]; @@ -809,7 +809,7 @@ fn resolveRelocsArm64( .ARM64_RELOC_PAGEOFF12 => { const adjusted_target_addr = @as(u64, @intCast(@as(i64, @intCast(target_addr)) + (addend orelse 0))); - log.debug(" | target_addr = 0x{x}", .{adjusted_target_addr}); + relocs_log.debug(" | target_addr = 0x{x}", .{adjusted_target_addr}); const code = atom_code[rel_offset..][0..4]; if (Relocation.isArithmeticOp(code)) { @@ -848,7 +848,7 @@ fn resolveRelocsArm64( const code = atom_code[rel_offset..][0..4]; const adjusted_target_addr = @as(u64, @intCast(@as(i64, @intCast(target_addr)) + (addend orelse 0))); - log.debug(" | target_addr = 0x{x}", .{adjusted_target_addr}); + relocs_log.debug(" | target_addr = 0x{x}", .{adjusted_target_addr}); const off = try Relocation.calcPageOffset(adjusted_target_addr, .load_store_64); var inst: aarch64.Instruction = .{ @@ -866,7 +866,7 @@ fn resolveRelocsArm64( const code = atom_code[rel_offset..][0..4]; const adjusted_target_addr = @as(u64, @intCast(@as(i64, @intCast(target_addr)) + (addend orelse 0))); - log.debug(" | target_addr = 0x{x}", .{adjusted_target_addr}); + relocs_log.debug(" | target_addr = 0x{x}", .{adjusted_target_addr}); const RegInfo = struct { rd: u5, @@ -923,7 +923,7 @@ fn resolveRelocsArm64( }, .ARM64_RELOC_POINTER_TO_GOT => { - log.debug(" | target_addr = 0x{x}", .{target_addr}); + relocs_log.debug(" | target_addr = 0x{x}", .{target_addr}); const result = math.cast(i32, @as(i64, @intCast(target_addr)) - @as(i64, @intCast(source_addr))) orelse return error.Overflow; mem.writeIntLittle(u32, atom_code[rel_offset..][0..4], @as(u32, @bitCast(result))); @@ -951,7 +951,7 @@ fn resolveRelocsArm64( break :blk @as(i64, @intCast(target_addr)) + ptr_addend; } }; - log.debug(" | target_addr = 0x{x}", .{result}); + relocs_log.debug(" | target_addr = 0x{x}", .{result}); if (rel.r_length == 3) { mem.writeIntLittle(u64, atom_code[rel_offset..][0..8], @as(u64, @bitCast(result))); @@ -987,7 +987,7 @@ fn resolveRelocsX86( .X86_64_RELOC_SUBTRACTOR => { assert(subtractor == null); - log.debug(" RELA({s}) @ {x} => %{d} in object({?d})", .{ + relocs_log.debug(" RELA({s}) @ {x} => %{d} in object({?d})", .{ @tagName(rel_type), rel.r_address, rel.r_symbolnum, @@ -1015,7 +1015,7 @@ fn resolveRelocsX86( }); const rel_offset = @as(u32, @intCast(rel.r_address - context.base_offset)); - log.debug(" RELA({s}) @ {x} => %{d} ('{s}') in object({?})", .{ + relocs_log.debug(" RELA({s}) @ {x} => %{d} ('{s}') in object({?})", .{ @tagName(rel_type), rel.r_address, target.sym_index, @@ -1041,13 +1041,13 @@ fn resolveRelocsX86( break :blk getRelocTargetAddress(macho_file, target, is_tlv); }; - log.debug(" | source_addr = 0x{x}", .{source_addr}); + relocs_log.debug(" | source_addr = 0x{x}", .{source_addr}); switch (rel_type) { .X86_64_RELOC_BRANCH => { const addend = mem.readIntLittle(i32, atom_code[rel_offset..][0..4]); const adjusted_target_addr = @as(u64, @intCast(@as(i64, @intCast(target_addr)) + addend)); - log.debug(" | target_addr = 0x{x}", .{adjusted_target_addr}); + relocs_log.debug(" | target_addr = 0x{x}", .{adjusted_target_addr}); const disp = try Relocation.calcPcRelativeDisplacementX86(source_addr, adjusted_target_addr, 0); mem.writeIntLittle(i32, atom_code[rel_offset..][0..4], disp); }, @@ -1057,7 +1057,7 @@ fn resolveRelocsX86( => { const addend = mem.readIntLittle(i32, atom_code[rel_offset..][0..4]); const adjusted_target_addr = @as(u64, @intCast(@as(i64, @intCast(target_addr)) + addend)); - log.debug(" | target_addr = 0x{x}", .{adjusted_target_addr}); + relocs_log.debug(" | target_addr = 0x{x}", .{adjusted_target_addr}); const disp = try Relocation.calcPcRelativeDisplacementX86(source_addr, adjusted_target_addr, 0); mem.writeIntLittle(i32, atom_code[rel_offset..][0..4], disp); }, @@ -1065,7 +1065,7 @@ fn resolveRelocsX86( .X86_64_RELOC_TLV => { const addend = mem.readIntLittle(i32, atom_code[rel_offset..][0..4]); const adjusted_target_addr = @as(u64, @intCast(@as(i64, @intCast(target_addr)) + addend)); - log.debug(" | target_addr = 0x{x}", .{adjusted_target_addr}); + relocs_log.debug(" | target_addr = 0x{x}", .{adjusted_target_addr}); const disp = try Relocation.calcPcRelativeDisplacementX86(source_addr, adjusted_target_addr, 0); if (macho_file.tlv_ptr_table.lookup.get(target) == null) { @@ -1101,7 +1101,7 @@ fn resolveRelocsX86( const adjusted_target_addr = @as(u64, @intCast(@as(i64, @intCast(target_addr)) + addend)); - log.debug(" | target_addr = 0x{x}", .{adjusted_target_addr}); + relocs_log.debug(" | target_addr = 0x{x}", .{adjusted_target_addr}); const disp = try Relocation.calcPcRelativeDisplacementX86(source_addr, adjusted_target_addr, correction); mem.writeIntLittle(i32, atom_code[rel_offset..][0..4], disp); @@ -1129,7 +1129,7 @@ fn resolveRelocsX86( break :blk @as(i64, @intCast(target_addr)) + addend; } }; - log.debug(" | target_addr = 0x{x}", .{result}); + relocs_log.debug(" | target_addr = 0x{x}", .{result}); if (rel.r_length == 3) { mem.writeIntLittle(u64, atom_code[rel_offset..][0..8], @as(u64, @bitCast(result))); @@ -1247,6 +1247,7 @@ const build_options = @import("build_options"); const aarch64 = @import("../../arch/aarch64/bits.zig"); const assert = std.debug.assert; const log = std.log.scoped(.link); +const relocs_log = std.log.scoped(.link_relocs); const macho = std.macho; const math = std.math; const mem = std.mem; diff --git a/src/link/MachO/Relocation.zig b/src/link/MachO/Relocation.zig index c75eaba855..5abef9a5ed 100644 --- a/src/link/MachO/Relocation.zig +++ b/src/link/MachO/Relocation.zig @@ -99,7 +99,7 @@ pub fn resolve(self: Relocation, macho_file: *MachO, atom_index: Atom.Index, cod else => @as(i64, @intCast(target_base_addr)) + self.addend, }; - log.debug(" ({x}: [() => 0x{x} ({s})) ({s})", .{ + relocs_log.debug(" ({x}: [() => 0x{x} ({s})) ({s})", .{ source_addr, target_addr, macho_file.getSymbolName(self.target), @@ -256,7 +256,7 @@ const Relocation = @This(); const std = @import("std"); const aarch64 = @import("../../arch/aarch64/bits.zig"); const assert = std.debug.assert; -const log = std.log.scoped(.link); +const relocs_log = std.log.scoped(.link_relocs); const macho = std.macho; const math = std.math; const mem = std.mem; diff --git a/src/link/MachO/zld.zig b/src/link/MachO/zld.zig index 595c64436d..6decd082de 100644 --- a/src/link/MachO/zld.zig +++ b/src/link/MachO/zld.zig @@ -390,9 +390,7 @@ pub fn linkWithZld( try macho_file.parseDependentLibs(&dependent_libs); - var actions = std.ArrayList(MachO.ResolveAction).init(gpa); - defer actions.deinit(); - try macho_file.resolveSymbols(&actions); + try macho_file.resolveSymbols(); if (macho_file.unresolved.count() > 0) { try macho_file.reportUndefined(); return error.FlushFailure; |
