diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index bd63231890..e535894604 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/src/arch/x86_64/CodeGen.zig @@ -8156,6 +8156,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier if (self.bin_file.cast(link.File.Elf)) |elf_file| { const sym_index = try elf_file.getOrCreateMetadataForDecl(owner_decl); const sym = elf_file.symbol(sym_index); + sym.flags.needs_got = true; _ = try sym.getOrCreateGotEntry(elf_file); const got_addr = sym.gotAddress(elf_file); try self.asmMemory(.{ ._, .call }, Memory.sib(.qword, .{ @@ -10234,6 +10235,7 @@ fn genLazySymbolRef( const sym_index = elf_file.getOrCreateMetadataForLazySymbol(lazy_sym) catch |err| return self.fail("{s} creating lazy symbol", .{@errorName(err)}); const sym = elf_file.symbol(sym_index); + sym.flags.needs_got = true; _ = try sym.getOrCreateGotEntry(elf_file); const got_addr = sym.gotAddress(elf_file); const got_mem = diff --git a/src/codegen.zig b/src/codegen.zig index 7c6dcbdc46..cc53a903cd 100644 --- a/src/codegen.zig +++ b/src/codegen.zig @@ -856,6 +856,7 @@ fn genDeclRef( if (bin_file.cast(link.File.Elf)) |elf_file| { const sym_index = try elf_file.getOrCreateMetadataForDecl(decl_index); const sym = elf_file.symbol(sym_index); + sym.flags.needs_got = true; _ = try sym.getOrCreateGotEntry(elf_file); return GenResult.mcv(.{ .memory = sym.gotAddress(elf_file) }); } else if (bin_file.cast(link.File.MachO)) |macho_file| { diff --git a/src/link/Elf.zig b/src/link/Elf.zig index efd3dc9d54..994cf14089 100644 --- a/src/link/Elf.zig +++ b/src/link/Elf.zig @@ -1049,8 +1049,7 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node self.resolveSymbols(); self.markImportsExports(); self.claimUnresolved(); - - if (self.unresolved.keys().len > 0) try self.reportUndefined(); + try self.scanRelocs(); self.allocateLinkerDefinedSymbols(); @@ -1381,6 +1380,28 @@ fn claimUnresolved(self: *Elf) void { } } +fn scanRelocs(self: *Elf) !void { + if (self.zig_module_index) |index| { + const zig_module = self.file(index).?.zig_module; + try zig_module.scanRelocs(self); + } + for (self.objects.items) |index| { + const object = self.file(index).?.object; + try object.scanRelocs(self); + } + + // try self.reportUndefined(); + + for (self.symbols.items) |*sym| { + if (sym.flags.needs_got) { + log.debug("'{s}' needs GOT", .{sym.name(self)}); + // TODO how can we tell we need to write it again, aka the entry is dirty? + const gop = try sym.getOrCreateGotEntry(self); + try self.got.writeEntry(self, gop.index); + } + } +} + fn linkWithLLD(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node) !void { const tracy = trace(@src()); defer tracy.end(); @@ -2375,8 +2396,9 @@ fn updateDeclCode( sym.value = atom_ptr.value; esym.st_value = atom_ptr.value; - const got_index = try sym.getOrCreateGotEntry(self); - try self.got.writeEntry(self, got_index); + sym.flags.needs_got = true; + const gop = try sym.getOrCreateGotEntry(self); + try self.got.writeEntry(self, gop.index); } const phdr_index = self.phdr_to_shdr_table.get(shdr_index).?; @@ -2609,8 +2631,9 @@ fn updateLazySymbol(self: *Elf, sym: link.File.LazySymbol, symbol_index: Symbol. local_sym.value = atom_ptr.value; local_esym.st_value = atom_ptr.value; - const got_index = try local_sym.getOrCreateGotEntry(self); - try self.got.writeEntry(self, got_index); + local_sym.flags.needs_got = true; + const gop = try local_sym.getOrCreateGotEntry(self); + try self.got.writeEntry(self, gop.index); const section_offset = atom_ptr.value - self.phdrs.items[phdr_index].p_vaddr; const file_offset = self.shdrs.items[local_sym.output_section_index].sh_offset + section_offset; diff --git a/src/link/Elf/Atom.zig b/src/link/Elf/Atom.zig index 6a029d97a2..d3a8de1302 100644 --- a/src/link/Elf/Atom.zig +++ b/src/link/Elf/Atom.zig @@ -311,6 +311,57 @@ pub fn freeRelocs(self: Atom, elf_file: *Elf) void { zig_module.relocs.items[self.relocs_section_index].clearRetainingCapacity(); } +pub fn scanRelocs(self: Atom, elf_file: *Elf) !void { + const file_ptr = elf_file.file(self.file_index).?; + const rels = self.relocs(elf_file); + var i: usize = 0; + while (i < rels.len) : (i += 1) { + const rel = rels[i]; + + if (rel.r_type() == elf.R_X86_64_NONE) continue; + + const symbol = switch (file_ptr) { + .zig_module => elf_file.symbol(rel.r_sym()), + .object => |x| elf_file.symbol(x.symbols.items[rel.r_sym()]), + else => unreachable, + }; + + // While traversing relocations, mark symbols that require special handling such as + // pointer indirection via GOT, or a stub trampoline via PLT. + switch (rel.r_type()) { + elf.R_X86_64_64 => {}, + + elf.R_X86_64_32, + elf.R_X86_64_32S, + => {}, + + elf.R_X86_64_GOT32, + elf.R_X86_64_GOT64, + elf.R_X86_64_GOTPC32, + elf.R_X86_64_GOTPC64, + elf.R_X86_64_GOTPCREL, + elf.R_X86_64_GOTPCREL64, + elf.R_X86_64_GOTPCRELX, + elf.R_X86_64_REX_GOTPCRELX, + => { + symbol.flags.needs_got = true; + }, + + elf.R_X86_64_PLT32, + elf.R_X86_64_PLTOFF64, + => { + if (symbol.flags.import) { + symbol.flags.needs_plt = true; + } + }, + + elf.R_X86_64_PC32 => {}, + + else => @panic("TODO"), + } + } +} + /// TODO mark relocs dirty pub fn resolveRelocs(self: Atom, elf_file: *Elf, code: []u8) !void { relocs_log.debug("0x{x}: {s}", .{ self.value, self.name(elf_file) }); @@ -484,6 +535,9 @@ fn format2( } } +// TODO this has to be u32 but for now, to avoid redesigning elfSym machinery for +// ZigModule, keep it at u16 with the intention of bumping it to u32 in the near +// future. pub const Index = u16; const std = @import("std"); diff --git a/src/link/Elf/Object.zig b/src/link/Elf/Object.zig index e99f7c63f8..527b2e5c61 100644 --- a/src/link/Elf/Object.zig +++ b/src/link/Elf/Object.zig @@ -367,15 +367,16 @@ pub fn scanRelocs(self: *Object, elf_file: *Elf) !void { } for (self.cies.items) |cie| { - for (cie.getRelocs(elf_file)) |rel| { + for (cie.relocs(elf_file)) |rel| { const sym = elf_file.symbol(self.symbols.items[rel.r_sym()]); if (sym.flags.import) { - if (sym.getType(elf_file) != elf.STT_FUNC) - elf_file.base.fatal("{s}: {s}: CIE referencing external data reference", .{ + if (sym.type(elf_file) != elf.STT_FUNC) + // TODO convert into an error + log.debug("{s}: {s}: CIE referencing external data reference", .{ self.fmtPath(), - sym.getName(elf_file), + sym.name(elf_file), }); - sym.flags.plt = true; + sym.flags.needs_plt = true; } } } diff --git a/src/link/Elf/Symbol.zig b/src/link/Elf/Symbol.zig index db7e69d5e4..18c3e9021e 100644 --- a/src/link/Elf/Symbol.zig +++ b/src/link/Elf/Symbol.zig @@ -111,19 +111,23 @@ pub fn address(symbol: Symbol, opts: struct { } pub fn gotAddress(symbol: Symbol, elf_file: *Elf) u64 { - if (!symbol.flags.got) return 0; + if (!symbol.flags.has_got) return 0; const extras = symbol.extra(elf_file).?; const entry = elf_file.got.entries.items[extras.got]; return entry.address(elf_file); } -pub fn getOrCreateGotEntry(symbol: *Symbol, elf_file: *Elf) !GotSection.Index { - const index = if (symbol.flags.got) - symbol.extra(elf_file).?.got - else - try elf_file.got.addGotSymbol(symbol.index, elf_file); - symbol.flags.got = true; - return index; +const GetOrCreateGotEntryResult = struct { + found_existing: bool, + index: GotSection.Index, +}; + +pub fn getOrCreateGotEntry(symbol: *Symbol, elf_file: *Elf) !GetOrCreateGotEntryResult { + assert(symbol.flags.needs_got); + if (symbol.flags.has_got) return .{ .found_existing = true, .index = symbol.extra(elf_file).?.got }; + const index = try elf_file.got.addGotSymbol(symbol.index, elf_file); + symbol.flags.has_got = true; + return .{ .found_existing = false, .index = index }; } // pub fn tlsGdAddress(symbol: Symbol, elf_file: *Elf) u64 { @@ -310,9 +314,11 @@ pub const Flags = packed struct { output_symtab: bool = false, /// Whether the symbol contains GOT indirection. - got: bool = false, + needs_got: bool = false, + has_got: bool = false, /// Whether the symbol contains PLT indirection. + needs_plt: bool = false, plt: bool = false, /// Whether the PLT entry is canonical. is_canonical: bool = false, diff --git a/src/link/Elf/ZigModule.zig b/src/link/Elf/ZigModule.zig index 75fd3842e2..5d231dc02a 100644 --- a/src/link/Elf/ZigModule.zig +++ b/src/link/Elf/ZigModule.zig @@ -130,6 +130,14 @@ pub fn claimUnresolved(self: *ZigModule, elf_file: *Elf) void { } } +pub fn scanRelocs(self: *ZigModule, elf_file: *Elf) !void { + for (self.atoms.keys()) |atom_index| { + const atom = elf_file.atom(atom_index) orelse continue; + if (!atom.alive) continue; + try atom.scanRelocs(elf_file); + } +} + pub fn updateSymtabSize(self: *ZigModule, elf_file: *Elf) void { for (self.locals()) |local_index| { const local = elf_file.symbol(local_index);