From 6ad5db030c576ab9cf944b4a9432954681af649c Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Fri, 8 Sep 2023 18:12:53 +0200 Subject: [PATCH] elf: store GOT index in symbol extra array; use GotSection for GOT --- src/arch/aarch64/CodeGen.zig | 4 +- src/arch/arm/CodeGen.zig | 4 +- src/arch/riscv64/CodeGen.zig | 4 +- src/arch/sparc64/CodeGen.zig | 4 +- src/arch/x86_64/CodeGen.zig | 8 +- src/codegen.zig | 4 +- src/link/Elf.zig | 134 ++++---- src/link/Elf/Symbol.zig | 42 ++- src/link/Elf/synthetic_sections.zig | 469 ++++++++++++++++++++++++++++ 9 files changed, 565 insertions(+), 108 deletions(-) create mode 100644 src/link/Elf/synthetic_sections.zig diff --git a/src/arch/aarch64/CodeGen.zig b/src/arch/aarch64/CodeGen.zig index 35daa4a02a1b7d0b387920f91135d884a186d68c..2c6e3f5a7b488e0995dc1226e8cc98115e050984 100644 --- a/src/arch/aarch64/CodeGen.zig +++ b/src/arch/aarch64/CodeGen.zig @@ -4316,8 +4316,8 @@ 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(func.owner_decl); const sym = elf_file.symbol(sym_index); - _ = try sym.getOrCreateOffsetTableEntry(elf_file); - const got_addr = @as(u32, @intCast(sym.getOffsetTableAddress(elf_file))); + _ = try sym.getOrCreateGotEntry(elf_file); + const got_addr = @as(u32, @intCast(sym.gotAddress(elf_file))); try self.genSetReg(Type.usize, .x30, .{ .memory = got_addr }); } else if (self.bin_file.cast(link.File.MachO)) |macho_file| { const atom = try macho_file.getOrCreateAtomForDecl(func.owner_decl); diff --git a/src/arch/arm/CodeGen.zig b/src/arch/arm/CodeGen.zig index f32e104c8852d89e53db92a1b80cefcd1839bf5d..937d4aa1dd4c09a3a72db69f0398edba47f1edb2 100644 --- a/src/arch/arm/CodeGen.zig +++ b/src/arch/arm/CodeGen.zig @@ -4296,8 +4296,8 @@ 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(func.owner_decl); const sym = elf_file.symbol(sym_index); - _ = try sym.getOrCreateOffsetTableEntry(elf_file); - const got_addr = @as(u32, @intCast(sym.getOffsetTableAddress(elf_file))); + _ = try sym.getOrCreateGotEntry(elf_file); + const got_addr = @as(u32, @intCast(sym.gotAddress(elf_file))); try self.genSetReg(Type.usize, .lr, .{ .memory = got_addr }); } else if (self.bin_file.cast(link.File.MachO)) |_| { unreachable; // unsupported architecture for MachO diff --git a/src/arch/riscv64/CodeGen.zig b/src/arch/riscv64/CodeGen.zig index 5ed60dc220616b9ecc3f3ca391a717935dc888bb..d3f162ae0f24c645f0c998f56615b4036b16b9c8 100644 --- a/src/arch/riscv64/CodeGen.zig +++ b/src/arch/riscv64/CodeGen.zig @@ -1749,8 +1749,8 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier .func => |func| { const sym_index = try elf_file.getOrCreateMetadataForDecl(func.owner_decl); const sym = elf_file.symbol(sym_index); - _ = try sym.getOrCreateOffsetTableEntry(elf_file); - const got_addr = @as(u32, @intCast(sym.getOffsetTableAddress(elf_file))); + _ = try sym.getOrCreateGotEntry(elf_file); + const got_addr = @as(u32, @intCast(sym.gotAddress(elf_file))); try self.genSetReg(Type.usize, .ra, .{ .memory = got_addr }); _ = try self.addInst(.{ .tag = .jalr, diff --git a/src/arch/sparc64/CodeGen.zig b/src/arch/sparc64/CodeGen.zig index b0a3573ff931296dbeef70dac226e036c706e22b..df1a7cf970763c0ce182f869a4dc93cff9058dd2 100644 --- a/src/arch/sparc64/CodeGen.zig +++ b/src/arch/sparc64/CodeGen.zig @@ -1351,8 +1351,8 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier const got_addr = if (self.bin_file.cast(link.File.Elf)) |elf_file| blk: { const sym_index = try elf_file.getOrCreateMetadataForDecl(func.owner_decl); const sym = elf_file.symbol(sym_index); - _ = try sym.getOrCreateOffsetTableEntry(elf_file); - break :blk @as(u32, @intCast(sym.getOffsetTableAddress(elf_file))); + _ = try sym.getOrCreateGotEntry(elf_file); + break :blk @as(u32, @intCast(sym.gotAddress(elf_file))); } else unreachable; try self.genSetReg(Type.usize, .o7, .{ .memory = got_addr }); diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index eaed79ef5e96e7b578b068c35f7a380c29fa662c..07bc4c9200b8f388d4c148536ef067d807eaa6d1 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/src/arch/x86_64/CodeGen.zig @@ -8151,8 +8151,8 @@ 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); - _ = try sym.getOrCreateOffsetTableEntry(elf_file); - const got_addr = sym.getOffsetTableAddress(elf_file); + _ = try sym.getOrCreateGotEntry(elf_file); + const got_addr = sym.gotAddress(elf_file); try self.asmMemory(.{ ._, .call }, Memory.sib(.qword, .{ .base = .{ .reg = .ds }, .disp = @intCast(got_addr), @@ -10218,8 +10218,8 @@ 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); - _ = try sym.getOrCreateOffsetTableEntry(elf_file); - const got_addr = sym.getOffsetTableAddress(elf_file); + _ = try sym.getOrCreateGotEntry(elf_file); + const got_addr = sym.gotAddress(elf_file); const got_mem = Memory.sib(.qword, .{ .base = .{ .reg = .ds }, .disp = @intCast(got_addr) }); switch (tag) { diff --git a/src/codegen.zig b/src/codegen.zig index d3f44bb2f556d8de68e69078a7f6062d40a9c568..7c6dcbdc46046d14f8bfb972be8a541658e97c18 100644 --- a/src/codegen.zig +++ b/src/codegen.zig @@ -856,8 +856,8 @@ 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); - _ = try sym.getOrCreateOffsetTableEntry(elf_file); - return GenResult.mcv(.{ .memory = sym.getOffsetTableAddress(elf_file) }); + _ = try sym.getOrCreateGotEntry(elf_file); + return GenResult.mcv(.{ .memory = sym.gotAddress(elf_file) }); } else if (bin_file.cast(link.File.MachO)) |macho_file| { const atom_index = try macho_file.getOrCreateAtomForDecl(decl_index); const sym_index = macho_file.getAtom(atom_index).getSymbolIndex().?; diff --git a/src/link/Elf.zig b/src/link/Elf.zig index 3893dc947d0f5d76c0ccb8f13108c8a053740ab8..ccd30cf1dd994920eab738463a14041d975c67a8 100644 --- a/src/link/Elf.zig +++ b/src/link/Elf.zig @@ -42,6 +42,8 @@ shstrtab: StringTable(.strtab) = .{}, /// .strtab buffer strtab: StringTable(.strtab) = .{}, +got: GotSection = .{}, + symtab_section_index: ?u16 = null, text_section_index: ?u16 = null, rodata_section_index: ?u16 = null, @@ -56,18 +58,16 @@ shstrtab_section_index: ?u16 = null, strtab_section_index: ?u16 = null, symbols: std.ArrayListUnmanaged(Symbol) = .{}, +symbols_extra: std.ArrayListUnmanaged(u32) = .{}, resolver: std.AutoArrayHashMapUnmanaged(u32, Symbol.Index) = .{}, unresolved: std.AutoArrayHashMapUnmanaged(u32, void) = .{}, symbols_free_list: std.ArrayListUnmanaged(Symbol.Index) = .{}, -got_table: TableSection(Symbol.Index) = .{}, - phdr_table_dirty: bool = false, shdr_table_dirty: bool = false, shstrtab_dirty: bool = false, strtab_dirty: bool = false, -got_table_count_dirty: bool = false, debug_strtab_dirty: bool = false, debug_abbrev_section_dirty: bool = false, @@ -146,6 +146,8 @@ pub fn openPath(allocator: Allocator, sub_path: []const u8, options: link.Option // Index 0 is always a null symbol. try self.symbols.append(allocator, .{}); + // Index 0 is always a null symbol. + try self.symbols_extra.append(allocator, 0); // Allocate atom index 0 to null atom try self.atoms.append(allocator, .{}); // Append null file at index 0 @@ -234,8 +236,9 @@ pub fn deinit(self: *Elf) void { self.shstrtab.deinit(gpa); self.strtab.deinit(gpa); self.symbols.deinit(gpa); + self.symbols_extra.deinit(gpa); self.symbols_free_list.deinit(gpa); - self.got_table.deinit(gpa); + self.got.deinit(gpa); self.resolver.deinit(gpa); self.unresolved.deinit(gpa); @@ -1249,7 +1252,7 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node assert(!self.shstrtab_dirty); assert(!self.strtab_dirty); assert(!self.debug_strtab_dirty); - assert(!self.got_table_count_dirty); + assert(!self.got.dirty); } fn linkWithLLD(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node) !void { @@ -2087,7 +2090,7 @@ fn freeDeclMetadata(self: *Elf, sym_index: Symbol.Index) void { log.debug("adding %{d} to local symbols free list", .{sym_index}); self.symbols_free_list.append(self.base.allocator, sym_index) catch {}; self.symbols.items[sym_index] = .{}; - self.got_table.freeEntry(self.base.allocator, sym_index); + // TODO free GOT entry here } pub fn freeDecl(self: *Elf, decl_index: Module.Decl.Index) void { @@ -2226,9 +2229,8 @@ fn updateDeclCode( esym.st_value = atom_ptr.value; log.debug(" (writing new offset table entry)", .{}); - const got_entry_index = self.got_table.lookup.get(sym_index).?; - self.got_table.entries.items[got_entry_index] = sym_index; - try self.writeOffsetTableEntry(got_entry_index); + const extra = sym.extra(self).?; + try self.got.writeEntry(self, extra.got); } } else if (code.len < old_size) { atom_ptr.shrink(self); @@ -2245,8 +2247,8 @@ fn updateDeclCode( sym.value = atom_ptr.value; esym.st_value = atom_ptr.value; - const got_entry_index = try sym.getOrCreateOffsetTableEntry(self); - try self.writeOffsetTableEntry(got_entry_index); + const got_index = try sym.getOrCreateGotEntry(self); + try self.got.writeEntry(self, got_index); } const phdr_index = self.sections.items(.phdr_index)[shdr_index]; @@ -2477,8 +2479,8 @@ 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_entry_index = try local_sym.getOrCreateOffsetTableEntry(self); - try self.writeOffsetTableEntry(got_entry_index); + const got_index = try local_sym.getOrCreateGotEntry(self); + try self.got.writeEntry(self, got_index); const section_offset = atom_ptr.value - self.program_headers.items[phdr_index].p_vaddr; const file_offset = self.sections.items(.shdr)[local_sym.output_section_index].sh_offset + section_offset; @@ -2712,61 +2714,6 @@ fn writeSectHeader(self: *Elf, index: usize) !void { } } -fn writeOffsetTableEntry(self: *Elf, index: @TypeOf(self.got_table).Index) !void { - const entry_size: u16 = self.archPtrWidthBytes(); - if (self.got_table_count_dirty) { - const needed_size = self.got_table.entries.items.len * entry_size; - try self.growAllocSection(self.got_section_index.?, needed_size); - self.got_table_count_dirty = false; - } - const endian = self.base.options.target.cpu.arch.endian(); - const shdr = &self.sections.items(.shdr)[self.got_section_index.?]; - const off = shdr.sh_offset + @as(u64, entry_size) * index; - const phdr = &self.program_headers.items[self.phdr_got_index.?]; - const vaddr = phdr.p_vaddr + @as(u64, entry_size) * index; - const got_entry = self.got_table.entries.items[index]; - const got_value = self.symbol(got_entry).value; - switch (entry_size) { - 2 => { - var buf: [2]u8 = undefined; - mem.writeInt(u16, &buf, @as(u16, @intCast(got_value)), endian); - try self.base.file.?.pwriteAll(&buf, off); - }, - 4 => { - var buf: [4]u8 = undefined; - mem.writeInt(u32, &buf, @as(u32, @intCast(got_value)), endian); - try self.base.file.?.pwriteAll(&buf, off); - }, - 8 => { - var buf: [8]u8 = undefined; - mem.writeInt(u64, &buf, got_value, endian); - try self.base.file.?.pwriteAll(&buf, off); - - if (self.base.child_pid) |pid| { - switch (builtin.os.tag) { - .linux => { - var local_vec: [1]std.os.iovec_const = .{.{ - .iov_base = &buf, - .iov_len = buf.len, - }}; - var remote_vec: [1]std.os.iovec_const = .{.{ - .iov_base = @as([*]u8, @ptrFromInt(@as(usize, @intCast(vaddr)))), - .iov_len = buf.len, - }}; - const rc = std.os.linux.process_vm_writev(pid, &local_vec, &remote_vec, 0); - switch (std.os.errno(rc)) { - .SUCCESS => assert(rc == buf.len), - else => |errno| log.warn("process_vm_writev failure: {s}", .{@tagName(errno)}), - } - }, - else => return error.HotSwapUnavailableOnHostOperatingSystem, - } - } - }, - else => unreachable, - } -} - fn updateSymtabSize(self: *Elf) !void { var sizes = SymtabSize{}; @@ -2858,8 +2805,8 @@ fn ptrWidthBytes(self: Elf) u8 { /// Does not necessarily match `ptrWidthBytes` for example can be 2 bytes /// in a 32-bit ELF file. -fn archPtrWidthBytes(self: Elf) u8 { - return @as(u8, @intCast(self.base.options.target.ptrBitWidth() / 8)); +pub fn archPtrWidthBytes(self: Elf) u8 { + return @as(u8, @intCast(@divExact(self.base.options.target.ptrBitWidth(), 8))); } fn progHeaderTo32(phdr: elf.Elf64_Phdr) elf.Elf32_Phdr { @@ -3179,6 +3126,50 @@ pub fn addSymbol(self: *Elf) !Symbol.Index { return index; } +pub fn addSymbolExtra(self: *Elf, extra: Symbol.Extra) !u32 { + const fields = @typeInfo(Symbol.Extra).Struct.fields; + try self.symbols_extra.ensureUnusedCapacity(self.base.allocator, fields.len); + return self.addSymbolExtraAssumeCapacity(extra); +} + +pub fn addSymbolExtraAssumeCapacity(self: *Elf, extra: Symbol.Extra) u32 { + const index = @as(u32, @intCast(self.symbols_extra.items.len)); + const fields = @typeInfo(Symbol.Extra).Struct.fields; + inline for (fields) |field| { + self.symbols_extra.appendAssumeCapacity(switch (field.type) { + u32 => @field(extra, field.name), + else => @compileError("bad field type"), + }); + } + return index; +} + +pub fn symbolExtra(self: *Elf, index: u32) ?Symbol.Extra { + if (index == 0) return null; + const fields = @typeInfo(Symbol.Extra).Struct.fields; + var i: usize = index; + var result: Symbol.Extra = undefined; + inline for (fields) |field| { + @field(result, field.name) = switch (field.type) { + u32 => self.symbols_extra.items[i], + else => @compileError("bad field type"), + }; + i += 1; + } + return result; +} + +pub fn setSymbolExtra(self: *Elf, index: u32, extra: Symbol.Extra) void { + assert(index > 0); + const fields = @typeInfo(Symbol.Extra).Struct.fields; + inline for (fields, 0..) |field, i| { + self.symbols_extra.items[index + i] = switch (field.type) { + u32 => @field(extra, field.name), + else => @compileError("bad field type"), + }; + } +} + const GetOrPutGlobalResult = struct { found_existing: bool, index: Symbol.Index, @@ -3227,6 +3218,7 @@ fn fmtDumpState( try writer.print("linker_defined({d}) : (linker defined)\n", .{index}); try writer.print("{}\n", .{linker_defined.fmtSymtab(self)}); } + try writer.print("{}\n", .{self.got.fmt(self)}); } const default_entry_addr = 0x8000000; @@ -3311,6 +3303,7 @@ const lldMain = @import("../main.zig").lldMain; const musl = @import("../musl.zig"); const target_util = @import("../target.zig"); const trace = @import("../tracy.zig").trace; +const synthetic_sections = @import("Elf/synthetic_sections.zig"); const Air = @import("../Air.zig"); const Allocator = std.mem.Allocator; @@ -3320,6 +3313,7 @@ const Compilation = @import("../Compilation.zig"); const Dwarf = @import("Dwarf.zig"); const Elf = @This(); const File = @import("Elf/file.zig").File; +const GotSection = synthetic_sections.GotSection; const LinkerDefined = @import("Elf/LinkerDefined.zig"); const Liveness = @import("../Liveness.zig"); const LlvmObject = @import("../codegen/llvm.zig").Object; diff --git a/src/link/Elf/Symbol.zig b/src/link/Elf/Symbol.zig index 33b159e70053a9f5c2f4aa3f7b77a9bf287cbc15..eb29d06fee1cee10da33b21ee7882154545ae946 100644 --- a/src/link/Elf/Symbol.zig +++ b/src/link/Elf/Symbol.zig @@ -84,24 +84,6 @@ pub fn symbolRank(symbol: Symbol, elf_file: *Elf) u32 { return file_ptr.symbolRank(sym, in_archive); } -/// If entry already exists, returns index to it. -/// Otherwise, creates a new entry in the Global Offset Table for this Symbol. -pub fn getOrCreateOffsetTableEntry(self: Symbol, elf_file: *Elf) !Symbol.Index { - if (elf_file.got_table.lookup.get(self.index)) |index| return index; - const index = try elf_file.got_table.allocateEntry(elf_file.base.allocator, self.index); - elf_file.got_table_count_dirty = true; - return index; -} - -pub fn getOffsetTableAddress(self: Symbol, elf_file: *Elf) u64 { - const got_entry_index = elf_file.got_table.lookup.get(self.index).?; - const target = elf_file.base.options.target; - const ptr_bits = target.ptrBitWidth(); - const ptr_bytes: u64 = @divExact(ptr_bits, 8); - const got = elf_file.program_headers.items[elf_file.phdr_got_index.?]; - return got.p_vaddr + got_entry_index * ptr_bytes; -} - pub fn address(symbol: Symbol, opts: struct { plt: bool = true, }, elf_file: *Elf) u64 { @@ -122,11 +104,21 @@ pub fn address(symbol: Symbol, opts: struct { return symbol.value; } -// pub fn gotAddress(symbol: Symbol, elf_file: *Elf) u64 { -// if (!symbol.flags.got) return 0; -// const extra = symbol.extra(elf_file).?; -// return elf_file.gotEntryAddress(extra.got); -// } +pub fn gotAddress(symbol: Symbol, elf_file: *Elf) u64 { + if (!symbol.flags.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; +} // pub fn tlsGdAddress(symbol: Symbol, elf_file: *Elf) u64 { // if (!symbol.flags.tlsgd) return 0; @@ -159,7 +151,7 @@ pub fn address(symbol: Symbol, opts: struct { // } pub fn addExtra(symbol: *Symbol, extras: Extra, elf_file: *Elf) !void { - symbol.extra = try elf_file.addSymbolExtra(extras); + symbol.extra_index = try elf_file.addSymbolExtra(extras); } pub fn extra(symbol: Symbol, elf_file: *Elf) ?Extra { @@ -347,10 +339,12 @@ pub const Index = u32; const assert = std.debug.assert; const elf = std.elf; const std = @import("std"); +const synthetic_sections = @import("synthetic_sections.zig"); const Atom = @import("Atom.zig"); const Elf = @import("../Elf.zig"); const File = @import("file.zig").File; +const GotSection = synthetic_sections.GotSection; const LinkerDefined = @import("LinkerDefined.zig"); // const Object = @import("Object.zig"); // const SharedObject = @import("SharedObject.zig"); diff --git a/src/link/Elf/synthetic_sections.zig b/src/link/Elf/synthetic_sections.zig new file mode 100644 index 0000000000000000000000000000000000000000..fce23aeddb15104d53dca8a5d97b840589a33fb4 --- /dev/null +++ b/src/link/Elf/synthetic_sections.zig @@ -0,0 +1,469 @@ +pub const GotSection = struct { + entries: std.ArrayListUnmanaged(Entry) = .{}, + needs_rela: bool = false, + dirty: bool = false, + output_symtab_size: Elf.SymtabSize = .{}, + + pub const Index = u32; + + const Tag = enum { + got, + tlsld, + tlsgd, + gottp, + tlsdesc, + }; + + const Entry = struct { + tag: Tag, + symbol_index: Symbol.Index, + cell_index: Index, + + /// Returns how many indexes in the GOT this entry uses. + pub inline fn len(entry: Entry) usize { + return switch (entry.tag) { + .got, .gottp => 1, + .tlsld, .tlsgd, .tlsdesc => 2, + }; + } + + pub fn address(entry: Entry, elf_file: *Elf) u64 { + const ptr_bytes = @as(u64, elf_file.archPtrWidthBytes()); + const shdr = &elf_file.sections.items(.shdr)[elf_file.got_section_index.?]; + return shdr.sh_addr + @as(u64, entry.cell_index) * ptr_bytes; + } + }; + + pub fn deinit(got: *GotSection, allocator: Allocator) void { + got.entries.deinit(allocator); + } + + fn allocateEntry(got: *GotSection, allocator: Allocator) !Index { + try got.entries.ensureUnusedCapacity(allocator, 1); + // TODO add free list + const index = @as(Index, @intCast(got.entries.items.len)); + const entry = got.entries.addOneAssumeCapacity(); + const cell_index: Index = if (index > 0) blk: { + const last = got.entries.items[index - 1]; + break :blk last.cell_index + @as(Index, @intCast(last.len())); + } else 0; + entry.* = .{ .tag = undefined, .symbol_index = undefined, .cell_index = cell_index }; + got.dirty = true; + return index; + } + + pub fn addGotSymbol(got: *GotSection, sym_index: Symbol.Index, elf_file: *Elf) !Index { + const index = try got.allocateEntry(elf_file.base.allocator); + const entry = &got.entries.items[index]; + entry.tag = .got; + entry.symbol_index = sym_index; + const symbol = elf_file.symbol(sym_index); + if (symbol.flags.import or symbol.isIFunc(elf_file) or (elf_file.base.options.pic and !symbol.isAbs(elf_file))) + got.needs_rela = true; + if (symbol.extra(elf_file)) |extra| { + var new_extra = extra; + new_extra.got = index; + symbol.setExtra(new_extra, elf_file); + } else try symbol.addExtra(.{ .got = index }, elf_file); + return index; + } + + // pub fn addTlsGdSymbol(got: *GotSection, sym_index: Symbol.Index, elf_file: *Elf) !void { + // const index = got.next_index; + // const symbol = elf_file.getSymbol(sym_index); + // if (symbol.flags.import or elf_file.options.output_mode == .lib) got.needs_rela = true; + // if (symbol.getExtra(elf_file)) |extra| { + // var new_extra = extra; + // new_extra.tlsgd = index; + // symbol.setExtra(new_extra, elf_file); + // } else try symbol.addExtra(.{ .tlsgd = index }, elf_file); + // try got.symbols.append(elf_file.base.allocator, .{ .tlsgd = sym_index }); + // got.next_index += 2; + // } + + // pub fn addGotTpSymbol(got: *GotSection, sym_index: Symbol.Index, elf_file: *Elf) !void { + // const index = got.next_index; + // const symbol = elf_file.getSymbol(sym_index); + // if (symbol.flags.import or elf_file.options.output_mode == .lib) got.needs_rela = true; + // if (symbol.getExtra(elf_file)) |extra| { + // var new_extra = extra; + // new_extra.gottp = index; + // symbol.setExtra(new_extra, elf_file); + // } else try symbol.addExtra(.{ .gottp = index }, elf_file); + // try got.symbols.append(elf_file.base.allocator, .{ .gottp = sym_index }); + // got.next_index += 1; + // } + + // pub fn addTlsDescSymbol(got: *GotSection, sym_index: Symbol.Index, elf_file: *Elf) !void { + // const index = got.next_index; + // const symbol = elf_file.getSymbol(sym_index); + // got.needs_rela = true; + // if (symbol.getExtra(elf_file)) |extra| { + // var new_extra = extra; + // new_extra.tlsdesc = index; + // symbol.setExtra(new_extra, elf_file); + // } else try symbol.addExtra(.{ .tlsdesc = index }, elf_file); + // try got.symbols.append(elf_file.base.allocator, .{ .tlsdesc = sym_index }); + // got.next_index += 2; + // } + + pub fn size(got: GotSection, elf_file: *Elf) usize { + var s: usize = 0; + for (got.entries.items) |entry| { + s += elf_file.archPtrWidthBytes() * entry.len(); + } + return s; + } + + pub fn writeEntry(got: *GotSection, elf_file: *Elf, index: Index) !void { + const entry_size: u16 = elf_file.archPtrWidthBytes(); + if (got.dirty) { + const needed_size = got.size(elf_file); + try elf_file.growAllocSection(elf_file.got_section_index.?, needed_size); + got.dirty = false; + } + const endian = elf_file.base.options.target.cpu.arch.endian(); + const entry = got.entries.items[index]; + const shdr = &elf_file.sections.items(.shdr)[elf_file.got_section_index.?]; + const off = shdr.sh_offset + @as(u64, entry_size) * entry.cell_index; + const vaddr = shdr.sh_addr + @as(u64, entry_size) * entry.cell_index; + const value = elf_file.symbol(entry.symbol_index).value; + switch (entry_size) { + 2 => { + var buf: [2]u8 = undefined; + std.mem.writeInt(u16, &buf, @as(u16, @intCast(value)), endian); + try elf_file.base.file.?.pwriteAll(&buf, off); + }, + 4 => { + var buf: [4]u8 = undefined; + std.mem.writeInt(u32, &buf, @as(u32, @intCast(value)), endian); + try elf_file.base.file.?.pwriteAll(&buf, off); + }, + 8 => { + var buf: [8]u8 = undefined; + std.mem.writeInt(u64, &buf, value, endian); + try elf_file.base.file.?.pwriteAll(&buf, off); + + if (elf_file.base.child_pid) |pid| { + switch (builtin.os.tag) { + .linux => { + var local_vec: [1]std.os.iovec_const = .{.{ + .iov_base = &buf, + .iov_len = buf.len, + }}; + var remote_vec: [1]std.os.iovec_const = .{.{ + .iov_base = @as([*]u8, @ptrFromInt(@as(usize, @intCast(vaddr)))), + .iov_len = buf.len, + }}; + const rc = std.os.linux.process_vm_writev(pid, &local_vec, &remote_vec, 0); + switch (std.os.errno(rc)) { + .SUCCESS => assert(rc == buf.len), + else => |errno| log.warn("process_vm_writev failure: {s}", .{@tagName(errno)}), + } + }, + else => return error.HotSwapUnavailableOnHostOperatingSystem, + } + } + }, + else => unreachable, + } + } + + // pub fn write(got: GotSection, elf_file: *Elf, writer: anytype) !void { + // const is_shared = elf_file.options.output_mode == .lib; + // const apply_relocs = elf_file.options.apply_dynamic_relocs; + + // for (got.symbols.items) |sym| { + // const symbol = elf_file.getSymbol(sym.getIndex()); + // switch (sym) { + // .got => { + // const value: u64 = blk: { + // const value = symbol.getAddress(.{ .plt = false }, elf_file); + // if (symbol.flags.import) break :blk 0; + // if (symbol.isIFunc(elf_file)) + // break :blk if (apply_relocs) value else 0; + // if (elf_file.options.pic and !symbol.isAbs(elf_file)) + // break :blk if (apply_relocs) value else 0; + // break :blk value; + // }; + // try writer.writeIntLittle(u64, value); + // }, + + // .tlsgd => { + // if (symbol.flags.import) { + // try writer.writeIntLittle(u64, 0); + // try writer.writeIntLittle(u64, 0); + // } else { + // try writer.writeIntLittle(u64, if (is_shared) @as(u64, 0) else 1); + // const offset = symbol.getAddress(.{}, elf_file) - elf_file.getDtpAddress(); + // try writer.writeIntLittle(u64, offset); + // } + // }, + + // .gottp => { + // if (symbol.flags.import) { + // try writer.writeIntLittle(u64, 0); + // } else if (is_shared) { + // const offset = if (apply_relocs) + // symbol.getAddress(.{}, elf_file) - elf_file.getTlsAddress() + // else + // 0; + // try writer.writeIntLittle(u64, offset); + // } else { + // const offset = @as(i64, @intCast(symbol.getAddress(.{}, elf_file))) - + // @as(i64, @intCast(elf_file.getTpAddress())); + // try writer.writeIntLittle(u64, @as(u64, @bitCast(offset))); + // } + // }, + + // .tlsdesc => { + // try writer.writeIntLittle(u64, 0); + // try writer.writeIntLittle(u64, 0); + // }, + // } + // } + + // if (got.emit_tlsld) { + // try writer.writeIntLittle(u64, if (is_shared) @as(u64, 0) else 1); + // try writer.writeIntLittle(u64, 0); + // } + // } + + // pub fn addRela(got: GotSection, elf_file: *Elf) !void { + // const is_shared = elf_file.options.output_mode == .lib; + // try elf_file.rela_dyn.ensureUnusedCapacity(elf_file.base.allocator, got.numRela(elf_file)); + + // for (got.symbols.items) |sym| { + // const symbol = elf_file.getSymbol(sym.getIndex()); + // const extra = symbol.getExtra(elf_file).?; + + // switch (sym) { + // .got => { + // const offset = symbol.gotAddress(elf_file); + + // if (symbol.flags.import) { + // elf_file.addRelaDynAssumeCapacity(.{ + // .offset = offset, + // .sym = extra.dynamic, + // .type = elf.R_X86_64_GLOB_DAT, + // }); + // continue; + // } + + // if (symbol.isIFunc(elf_file)) { + // elf_file.addRelaDynAssumeCapacity(.{ + // .offset = offset, + // .type = elf.R_X86_64_IRELATIVE, + // .addend = @intCast(symbol.getAddress(.{ .plt = false }, elf_file)), + // }); + // continue; + // } + + // if (elf_file.options.pic and !symbol.isAbs(elf_file)) { + // elf_file.addRelaDynAssumeCapacity(.{ + // .offset = offset, + // .type = elf.R_X86_64_RELATIVE, + // .addend = @intCast(symbol.getAddress(.{ .plt = false }, elf_file)), + // }); + // } + // }, + + // .tlsgd => { + // const offset = symbol.getTlsGdAddress(elf_file); + // if (symbol.flags.import) { + // elf_file.addRelaDynAssumeCapacity(.{ + // .offset = offset, + // .sym = extra.dynamic, + // .type = elf.R_X86_64_DTPMOD64, + // }); + // elf_file.addRelaDynAssumeCapacity(.{ + // .offset = offset + 8, + // .sym = extra.dynamic, + // .type = elf.R_X86_64_DTPOFF64, + // }); + // } else if (is_shared) { + // elf_file.addRelaDynAssumeCapacity(.{ + // .offset = offset, + // .sym = extra.dynamic, + // .type = elf.R_X86_64_DTPMOD64, + // }); + // } + // }, + + // .gottp => { + // const offset = symbol.getGotTpAddress(elf_file); + // if (symbol.flags.import) { + // elf_file.addRelaDynAssumeCapacity(.{ + // .offset = offset, + // .sym = extra.dynamic, + // .type = elf.R_X86_64_TPOFF64, + // }); + // } else if (is_shared) { + // elf_file.addRelaDynAssumeCapacity(.{ + // .offset = offset, + // .type = elf.R_X86_64_TPOFF64, + // .addend = @intCast(symbol.getAddress(.{}, elf_file) - elf_file.getTlsAddress()), + // }); + // } + // }, + + // .tlsdesc => { + // const offset = symbol.getTlsDescAddress(elf_file); + // elf_file.addRelaDynAssumeCapacity(.{ + // .offset = offset, + // .sym = extra.dynamic, + // .type = elf.R_X86_64_TLSDESC, + // }); + // }, + // } + // } + + // if (is_shared and got.emit_tlsld) { + // const offset = elf_file.getTlsLdAddress(); + // elf_file.addRelaDynAssumeCapacity(.{ + // .offset = offset, + // .type = elf.R_X86_64_DTPMOD64, + // }); + // } + // } + + // pub fn numRela(got: GotSection, elf_file: *Elf) usize { + // const is_shared = elf_file.options.output_mode == .lib; + // var num: usize = 0; + // for (got.symbols.items) |sym| { + // const symbol = elf_file.symbol(sym.index()); + // switch (sym) { + // .got => if (symbol.flags.import or + // symbol.isIFunc(elf_file) or (elf_file.options.pic and !symbol.isAbs(elf_file))) + // { + // num += 1; + // }, + + // .tlsgd => if (symbol.flags.import) { + // num += 2; + // } else if (is_shared) { + // num += 1; + // }, + + // .gottp => if (symbol.flags.import or is_shared) { + // num += 1; + // }, + + // .tlsdesc => num += 1, + // } + // } + // if (is_shared and got.emit_tlsld) num += 1; + // return num; + // } + + pub fn calcSymtabSize(got: *GotSection, elf_file: *Elf) !void { + got.output_symtab_size.nlocals = @as(u32, @intCast(got.symbols.items.len)); + for (got.symbols.items) |sym| { + const suffix_len = switch (sym) { + .tlsgd => "$tlsgd".len, + .got => "$got".len, + .gottp => "$gottp".len, + .tlsdesc => "$tlsdesc".len, + }; + const symbol = elf_file.getSymbol(sym.getIndex()); + const name_len = symbol.getName(elf_file).len; + got.output_symtab_size.strsize += @as(u32, @intCast(name_len + suffix_len + 1)); + } + + if (got.emit_tlsld) { + got.output_symtab_size.nlocals += 1; + got.output_symtab_size.strsize += @as(u32, @intCast("$tlsld".len + 1)); + } + } + + pub fn writeSymtab(got: GotSection, elf_file: *Elf, ctx: anytype) !void { + const gpa = elf_file.base.allocator; + + var ilocal = ctx.ilocal; + for (got.symbols.items) |sym| { + const suffix = switch (sym) { + .tlsgd => "$tlsgd", + .got => "$got", + .gottp => "$gottp", + .tlsdesc => "$tlsdesc", + }; + const symbol = elf_file.getSymbol(sym.getIndex()); + const name = try std.fmt.allocPrint(gpa, "{s}{s}", .{ symbol.getName(elf_file), suffix }); + defer gpa.free(name); + const st_name = try ctx.strtab.insert(gpa, name); + const st_value = switch (sym) { + // .tlsgd => symbol.tlsGdAddress(elf_file), + .got => symbol.gotAddress(elf_file), + // .gottp => symbol.gotTpAddress(elf_file), + // .tlsdesc => symbol.tlsDescAddress(elf_file), + else => unreachable, + }; + const st_size: u64 = switch (sym) { + .tlsgd, .tlsdesc => 16, + .got, .gottp => 8, + }; + ctx.symtab[ilocal] = .{ + .st_name = st_name, + .st_info = elf.STT_OBJECT, + .st_other = 0, + .st_shndx = elf_file.got_section_index.?, + .st_value = st_value, + .st_size = st_size, + }; + ilocal += 1; + } + + // if (got.emit_tlsld) { + // const st_name = try ctx.strtab.insert(gpa, "$tlsld"); + // ctx.symtab[ilocal] = .{ + // .st_name = st_name, + // .st_info = elf.STT_OBJECT, + // .st_other = 0, + // .st_shndx = elf_file.got_sect_index.?, + // .st_value = elf_file.getTlsLdAddress(), + // .st_size = 16, + // }; + // ilocal += 1; + // } + } + + const FormatCtx = struct { + got: GotSection, + elf_file: *Elf, + }; + + pub fn fmt(got: GotSection, elf_file: *Elf) std.fmt.Formatter(format2) { + return .{ .data = .{ .got = got, .elf_file = elf_file } }; + } + + pub fn format2( + ctx: FormatCtx, + comptime unused_fmt_string: []const u8, + options: std.fmt.FormatOptions, + writer: anytype, + ) !void { + _ = options; + _ = unused_fmt_string; + try writer.writeAll("GOT\n"); + for (ctx.got.entries.items) |entry| { + const symbol = ctx.elf_file.symbol(entry.symbol_index); + try writer.print(" {d}@0x{x} => {d}@0x{x} ({s})\n", .{ + entry.cell_index, + entry.address(ctx.elf_file), + entry.symbol_index, + symbol.address(.{}, ctx.elf_file), + symbol.name(ctx.elf_file), + }); + } + } +}; + +const assert = std.debug.assert; +const builtin = @import("builtin"); +const elf = std.elf; +const log = std.log.scoped(.link); +const std = @import("std"); + +const Allocator = std.mem.Allocator; +const Elf = @import("../Elf.zig"); +const Symbol = @import("Symbol.zig"); -- 2.54.0