elf: use zld's update mechanism for symtab for Zig module

This commit is contained in:
Jakub Konka 2023-09-07 15:45:58 +02:00
parent 37e2958f81
commit 9691d1a30f
3 changed files with 143 additions and 87 deletions

View File

@ -1043,7 +1043,8 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node
}
}
try self.writeSymbols();
try self.updateSymtabSize();
try self.writeSymtab();
if (build_options.enable_logging) {
state_log.debug("{}", .{self.dumpState()});
@ -2662,14 +2663,7 @@ pub fn deleteDeclExport(
assert(self.resolver.fetchSwapRemove(sym.name_offset) != null); // TODO don't delete it if it's not dominant
sym.* = .{};
// TODO free list for esym!
esym.* = .{
.st_name = 0,
.st_info = 0,
.st_other = 0,
.st_shndx = 0,
.st_value = 0,
.st_size = 0,
};
esym.* = null_sym;
self.symbols_free_list.append(gpa, sym_index.*) catch {};
sym_index.* = 0;
}
@ -2773,19 +2767,20 @@ fn writeOffsetTableEntry(self: *Elf, index: @TypeOf(self.got_table).Index) !void
}
}
fn elf32SymFromSym(sym: elf.Elf64_Sym, out: *elf.Elf32_Sym) void {
out.* = .{
.st_name = sym.st_name,
.st_value = @as(u32, @intCast(sym.st_value)),
.st_size = @as(u32, @intCast(sym.st_size)),
.st_info = sym.st_info,
.st_other = sym.st_other,
.st_shndx = sym.st_shndx,
};
}
fn updateSymtabSize(self: *Elf) !void {
var sizes = SymtabSize{};
if (self.zig_module_index) |index| {
const zig_module = self.file(index).?.zig_module;
zig_module.updateSymtabSize(self);
sizes.nlocals += zig_module.output_symtab_size.nlocals;
sizes.nglobals += zig_module.output_symtab_size.nglobals;
}
const shdr = &self.sections.items(.shdr)[self.symtab_section_index.?];
shdr.sh_info = sizes.nlocals + 1;
self.markDirty(self.symtab_section_index.?, null);
fn writeSymbols(self: *Elf) !void {
const gpa = self.base.allocator;
const sym_size: u64 = switch (self.ptr_width) {
.p32 => @sizeOf(elf.Elf32_Sym),
.p64 => @sizeOf(elf.Elf64_Sym),
@ -2794,59 +2789,61 @@ fn writeSymbols(self: *Elf) !void {
.p32 => @alignOf(elf.Elf32_Sym),
.p64 => @alignOf(elf.Elf64_Sym),
};
const zig_module = self.file(self.zig_module_index.?).?.zig_module;
const shdr = &self.sections.items(.shdr)[self.symtab_section_index.?];
shdr.sh_info = @intCast(zig_module.locals().len);
self.markDirty(self.symtab_section_index.?, null);
const nsyms = zig_module.locals().len + zig_module.globals().len;
const needed_size = nsyms * sym_size;
const needed_size = (sizes.nlocals + sizes.nglobals + 1) * sym_size;
try self.growNonAllocSection(self.symtab_section_index.?, needed_size, sym_align, true);
}
fn writeSymtab(self: *Elf) !void {
const gpa = self.base.allocator;
const shdr = &self.sections.items(.shdr)[self.symtab_section_index.?];
const sym_size: u64 = switch (self.ptr_width) {
.p32 => @sizeOf(elf.Elf32_Sym),
.p64 => @sizeOf(elf.Elf64_Sym),
};
const nsyms = @divExact(shdr.sh_size, sym_size);
log.debug("writing {d} symbols at 0x{x}", .{ nsyms, shdr.sh_offset });
const symtab = try gpa.alloc(elf.Elf64_Sym, nsyms);
defer gpa.free(symtab);
var ctx: struct { ilocal: usize, iglobal: usize, symtab: []elf.Elf64_Sym } = .{
.ilocal = 1,
.iglobal = shdr.sh_info,
.symtab = symtab,
};
if (self.zig_module_index) |index| {
const zig_module = self.file(index).?.zig_module;
zig_module.writeSymtab(self, ctx);
ctx.ilocal += zig_module.output_symtab_size.nlocals;
ctx.iglobal += zig_module.output_symtab_size.nglobals;
}
const foreign_endian = self.base.options.target.cpu.arch.endian() != builtin.cpu.arch.endian();
log.debug("writing {d} symbols at 0x{x}", .{ nsyms, shdr.sh_offset });
switch (self.ptr_width) {
.p32 => {
const buf = try gpa.alloc(elf.Elf32_Sym, nsyms);
const buf = try gpa.alloc(elf.Elf32_Sym, symtab.len);
defer gpa.free(buf);
for (buf[0..zig_module.locals().len], zig_module.locals()) |*sym, local_index| {
const local = self.symbol(local_index);
elf32SymFromSym(local.sourceSymbol(self).*, sym);
if (foreign_endian) {
mem.byteSwapAllFields(elf.Elf32_Sym, sym);
}
}
for (buf[zig_module.locals().len..], zig_module.globals()) |*sym, global_index| {
const global = self.symbol(global_index);
elf32SymFromSym(global.sourceSymbol(self).*, sym);
if (foreign_endian) {
mem.byteSwapAllFields(elf.Elf32_Sym, sym);
}
for (buf, symtab) |*out, sym| {
out.* = .{
.st_name = sym.st_name,
.st_info = sym.st_info,
.st_other = sym.st_other,
.st_shndx = sym.st_shndx,
.st_value = @as(u32, @intCast(sym.st_value)),
.st_size = @as(u32, @intCast(sym.st_size)),
};
if (foreign_endian) mem.byteSwapAllFields(elf.Elf32_Sym, out);
}
try self.base.file.?.pwriteAll(mem.sliceAsBytes(buf), shdr.sh_offset);
},
.p64 => {
const buf = try gpa.alloc(elf.Elf64_Sym, nsyms);
defer gpa.free(buf);
for (buf[0..zig_module.locals().len], zig_module.locals()) |*sym, local_index| {
const local = self.symbol(local_index);
sym.* = local.sourceSymbol(self).*;
if (foreign_endian) {
mem.byteSwapAllFields(elf.Elf64_Sym, sym);
}
if (foreign_endian) {
for (symtab) |*sym| mem.byteSwapAllFields(elf.Elf64_Sym, sym);
}
for (buf[zig_module.locals().len..], zig_module.globals()) |*sym, global_index| {
const global = self.symbol(global_index);
sym.* = global.sourceSymbol(self).*;
if (foreign_endian) {
mem.byteSwapAllFields(elf.Elf64_Sym, sym);
}
}
try self.base.file.?.pwriteAll(mem.sliceAsBytes(buf), shdr.sh_offset);
try self.base.file.?.pwriteAll(mem.sliceAsBytes(symtab), shdr.sh_offset);
},
}
}
@ -3282,7 +3279,19 @@ const DeclMetadata = struct {
}
};
const Elf = @This();
pub const SymtabSize = struct {
nlocals: u32 = 0,
nglobals: u32 = 0,
};
pub const null_sym = elf.Elf64_Sym{
.st_name = 0,
.st_info = 0,
.st_other = 0,
.st_shndx = 0,
.st_value = 0,
.st_size = 0,
};
const std = @import("std");
const build_options = @import("build_options");
@ -3309,6 +3318,7 @@ pub const Atom = @import("Elf/Atom.zig");
const Cache = std.Build.Cache;
const Compilation = @import("../Compilation.zig");
const Dwarf = @import("Dwarf.zig");
const Elf = @This();
const File = @import("Elf/file.zig").File;
const LinkerDefined = @import("Elf/LinkerDefined.zig");
const Liveness = @import("../Liveness.zig");

View File

@ -35,7 +35,8 @@ extra_index: u32 = 0,
pub fn isAbs(symbol: Symbol, elf_file: *Elf) bool {
const file_ptr = symbol.file(elf_file).?;
// if (file_ptr == .shared) return symbol.sourceSymbol(elf_file).st_shndx == elf.SHN_ABS;
return !symbol.flags.import and symbol.atom(elf_file) == null and symbol.output_section_index == 0 and file_ptr != .linker_defined and file_ptr != .zig_module;
return !symbol.flags.import and symbol.atom(elf_file) == null and symbol.output_section_index == 0 and
file_ptr != .linker_defined and file_ptr != .zig_module;
}
pub fn isLocal(symbol: Symbol) bool {
@ -169,7 +170,7 @@ pub fn setExtra(symbol: Symbol, extras: Extra, elf_file: *Elf) void {
elf_file.setSymbolExtra(symbol.extra_index, extras);
}
pub fn asElfSym(symbol: Symbol, st_name: u32, elf_file: *Elf) elf.Elf64_Sym {
pub fn setOutputSym(symbol: Symbol, elf_file: *Elf, out: *elf.Elf64_Sym) void {
const file_ptr = symbol.file(elf_file).?;
const s_sym = symbol.sourceSymbol(elf_file);
const st_type = symbol.type(elf_file);
@ -184,7 +185,7 @@ pub fn asElfSym(symbol: Symbol, st_name: u32, elf_file: *Elf) elf.Elf64_Sym {
// if (file_ptr == .shared or s_sym.st_shndx == elf.SHN_UNDEF) break :blk elf.SHN_UNDEF;
if (symbol.atom(elf_file) == null and file_ptr != .linker_defined and file_ptr != .zig_module)
break :blk elf.SHN_ABS;
break :blk symbol.shndx;
break :blk symbol.output_section_index;
};
const st_value = blk: {
// if (symbol.flags.copy_rel) break :blk symbol.address(.{}, elf_file);
@ -197,8 +198,8 @@ pub fn asElfSym(symbol: Symbol, st_name: u32, elf_file: *Elf) elf.Elf64_Sym {
// if (Elf.shdrIsTls(shdr)) break :blk symbol.value - elf_file.getTlsAddress();
break :blk symbol.value;
};
return elf.Elf64_Sym{
.st_name = st_name,
out.* = .{
.st_name = symbol.name_offset,
.st_info = (st_bind << 4) | st_type,
.st_other = s_sym.st_other,
.st_shndx = st_shndx,
@ -343,9 +344,9 @@ pub const Extra = struct {
pub const Index = u32;
const std = @import("std");
const assert = std.debug.assert;
const elf = std.elf;
const std = @import("std");
const Atom = @import("Atom.zig");
const Elf = @import("../Elf.zig");

View File

@ -10,7 +10,7 @@ atoms: std.ArrayListUnmanaged(Atom.Index) = .{},
alive: bool = true,
// output_symtab_size: Elf.SymtabSize = .{},
output_symtab_size: Elf.SymtabSize = .{},
pub fn deinit(self: *ZigModule, allocator: Allocator) void {
self.elf_local_symbols.deinit(allocator);
@ -36,14 +36,9 @@ pub fn createAtom(self: *ZigModule, output_section_index: u16, elf_file: *Elf) !
symbol_ptr.esym_index = @as(Symbol.Index, @intCast(self.elf_local_symbols.items.len));
const local_esym = try self.elf_local_symbols.addOne(gpa);
local_esym.* = .{
.st_name = 0,
.st_info = elf.STB_LOCAL << 4,
.st_other = 0,
.st_shndx = output_section_index,
.st_value = 0,
.st_size = 0,
};
local_esym.* = Elf.null_sym;
local_esym.st_info = elf.STB_LOCAL << 4;
local_esym.st_shndx = output_section_index;
try self.atoms.append(gpa, atom_index);
try self.local_symbols.putNoClobber(gpa, symbol_index, {});
@ -57,22 +52,73 @@ pub fn addGlobal(self: *ZigModule, name: [:0]const u8, elf_file: *Elf) !Symbol.I
try self.global_symbols.ensureUnusedCapacity(gpa, 1);
const off = try elf_file.strtab.insert(gpa, name);
const esym_index = @as(Symbol.Index, @intCast(self.elf_global_symbols.items.len));
self.elf_global_symbols.appendAssumeCapacity(.{
.st_name = off,
.st_info = elf.STB_GLOBAL << 4,
.st_other = 0,
.st_shndx = 0,
.st_value = 0,
.st_size = 0,
});
const esym = self.elf_global_symbols.addOneAssumeCapacity();
esym.* = Elf.null_sym;
esym.st_name = off;
esym.st_info = elf.STB_GLOBAL << 4;
const gop = try elf_file.getOrPutGlobal(off);
const sym = elf_file.symbol(gop.index);
sym.file_index = self.index;
sym.esym_index = esym_index;
sym.flags.@"export" = true;
self.global_symbols.putAssumeCapacityNoClobber(gop.index, {});
return gop.index;
}
pub fn updateSymtabSize(self: *ZigModule, elf_file: *Elf) void {
for (self.locals()) |local_index| {
const local = elf_file.symbol(local_index);
const esym = self.sourceSymbol(local_index, elf_file);
switch (esym.st_type()) {
elf.STT_SECTION, elf.STT_NOTYPE => {
local.flags.output_symtab = false;
continue;
},
else => {},
}
local.flags.output_symtab = true;
self.output_symtab_size.nlocals += 1;
}
for (self.globals()) |global_index| {
const global = elf_file.symbol(global_index);
if (global.file(elf_file)) |file| if (file.index() != self.index) {
global.flags.output_symtab = false;
continue;
};
global.flags.output_symtab = true;
if (global.isLocal()) {
self.output_symtab_size.nlocals += 1;
} else {
self.output_symtab_size.nglobals += 1;
}
}
}
pub fn writeSymtab(self: *ZigModule, elf_file: *Elf, ctx: anytype) void {
var ilocal = ctx.ilocal;
for (self.locals()) |local_index| {
const local = elf_file.symbol(local_index);
if (!local.flags.output_symtab) continue;
local.setOutputSym(elf_file, &ctx.symtab[ilocal]);
ilocal += 1;
}
var iglobal = ctx.iglobal;
for (self.globals()) |global_index| {
const global = elf_file.symbol(global_index);
if (global.file(elf_file)) |file| if (file.index() != self.index) continue;
if (!global.flags.output_symtab) continue;
if (global.isLocal()) {
global.setOutputSym(elf_file, &ctx.symtab[ilocal]);
ilocal += 1;
} else {
global.setOutputSym(elf_file, &ctx.symtab[iglobal]);
iglobal += 1;
}
}
}
pub fn sourceSymbol(self: *ZigModule, symbol_index: Symbol.Index, elf_file: *Elf) *elf.Elf64_Sym {
const sym = elf_file.symbol(symbol_index);
if (self.local_symbols.get(symbol_index)) |_| return &self.elf_local_symbols.items[sym.esym_index];
@ -134,5 +180,4 @@ const Elf = @import("../Elf.zig");
const File = @import("file.zig").File;
const Module = @import("../../Module.zig");
const ZigModule = @This();
// const Object = @import("Object.zig");
const Symbol = @import("Symbol.zig");