Commit 6ad5db030c
Changed files (9)
src
arch
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);
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
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,
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 });
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) {
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");
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");
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;
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().?;