zig/src/link/Elf/file.zig
2023-11-04 09:13:18 +01:00

231 lines
7.7 KiB
Zig

pub const File = union(enum) {
zig_object: *ZigObject,
linker_defined: *LinkerDefined,
object: *Object,
shared_object: *SharedObject,
pub fn index(file: File) Index {
return switch (file) {
inline else => |x| x.index,
};
}
pub fn fmtPath(file: File) std.fmt.Formatter(formatPath) {
return .{ .data = file };
}
fn formatPath(
file: File,
comptime unused_fmt_string: []const u8,
options: std.fmt.FormatOptions,
writer: anytype,
) !void {
_ = unused_fmt_string;
_ = options;
switch (file) {
.zig_object => |x| try writer.print("{s}", .{x.path}),
.linker_defined => try writer.writeAll("(linker defined)"),
.object => |x| try writer.print("{}", .{x.fmtPath()}),
.shared_object => |x| try writer.writeAll(x.path),
}
}
pub fn isAlive(file: File) bool {
return switch (file) {
.zig_object => true,
.linker_defined => true,
inline else => |x| x.alive,
};
}
/// Encodes symbol rank so that the following ordering applies:
/// * strong defined
/// * weak defined
/// * strong in lib (dso/archive)
/// * weak in lib (dso/archive)
/// * common
/// * common in lib (archive)
/// * unclaimed
pub fn symbolRank(file: File, sym: elf.Elf64_Sym, in_archive: bool) u32 {
const base: u3 = blk: {
if (sym.st_shndx == elf.SHN_COMMON) break :blk if (in_archive) 6 else 5;
if (file == .shared_object or in_archive) break :blk switch (sym.st_bind()) {
elf.STB_GLOBAL => 3,
else => 4,
};
break :blk switch (sym.st_bind()) {
elf.STB_GLOBAL => 1,
else => 2,
};
};
return (@as(u32, base) << 24) + file.index();
}
pub fn resolveSymbols(file: File, elf_file: *Elf) void {
switch (file) {
inline else => |x| x.resolveSymbols(elf_file),
}
}
pub fn resetGlobals(file: File, elf_file: *Elf) void {
for (file.globals()) |global_index| {
const global = elf_file.symbol(global_index);
const name_offset = global.name_offset;
global.* = .{};
global.name_offset = name_offset;
global.flags.global = true;
}
}
pub fn setAlive(file: File) void {
switch (file) {
.zig_object, .linker_defined => {},
inline else => |x| x.alive = true,
}
}
pub fn markLive(file: File, elf_file: *Elf) void {
switch (file) {
.linker_defined => {},
inline else => |x| x.markLive(elf_file),
}
}
pub fn atoms(file: File) []const Atom.Index {
return switch (file) {
.linker_defined, .shared_object => &[0]Atom.Index{},
.zig_object => |x| x.atoms.items,
.object => |x| x.atoms.items,
};
}
pub fn cies(file: File) []const Cie {
return switch (file) {
.zig_object => &[0]Cie{},
.object => |x| x.cies.items,
inline else => unreachable,
};
}
pub fn symbol(file: File, ind: Symbol.Index) Symbol.Index {
return switch (file) {
.zig_object => |x| x.symbol(ind),
inline else => |x| x.symbols.items[ind],
};
}
pub fn locals(file: File) []const Symbol.Index {
return switch (file) {
.linker_defined, .shared_object => &[0]Symbol.Index{},
inline else => |x| x.locals(),
};
}
pub fn globals(file: File) []const Symbol.Index {
return switch (file) {
inline else => |x| x.globals(),
};
}
pub fn updateSymtabSize(file: File, elf_file: *Elf) void {
const output_symtab_size = switch (file) {
inline else => |x| &x.output_symtab_size,
};
for (file.locals()) |local_index| {
const local = elf_file.symbol(local_index);
if (local.atom(elf_file)) |atom| if (!atom.flags.alive) continue;
const esym = local.elfSym(elf_file);
switch (esym.st_type()) {
elf.STT_SECTION => if (!elf_file.isRelocatable()) continue,
elf.STT_NOTYPE => continue,
else => {},
}
local.flags.output_symtab = true;
output_symtab_size.nlocals += 1;
output_symtab_size.strsize += @as(u32, @intCast(local.name(elf_file).len)) + 1;
}
for (file.globals()) |global_index| {
const global = elf_file.symbol(global_index);
const file_ptr = global.file(elf_file) orelse continue;
if (file_ptr.index() != file.index()) continue;
if (global.atom(elf_file)) |atom| if (!atom.flags.alive) continue;
global.flags.output_symtab = true;
if (global.isLocal(elf_file)) {
output_symtab_size.nlocals += 1;
} else {
output_symtab_size.nglobals += 1;
}
output_symtab_size.strsize += @as(u32, @intCast(global.name(elf_file).len)) + 1;
}
}
pub fn writeSymtab(file: File, elf_file: *Elf, ctx: anytype) void {
var ilocal = ctx.ilocal;
for (file.locals()) |local_index| {
const local = elf_file.symbol(local_index);
if (!local.flags.output_symtab) continue;
const out_sym = &elf_file.symtab.items[ilocal];
out_sym.st_name = @intCast(elf_file.strtab.items.len);
elf_file.strtab.appendSliceAssumeCapacity(local.name(elf_file));
elf_file.strtab.appendAssumeCapacity(0);
local.setOutputSym(elf_file, out_sym);
ilocal += 1;
}
var iglobal = ctx.iglobal;
for (file.globals()) |global_index| {
const global = elf_file.symbol(global_index);
const file_ptr = global.file(elf_file) orelse continue;
if (file_ptr.index() != file.index()) continue;
if (!global.flags.output_symtab) continue;
const st_name = @as(u32, @intCast(elf_file.strtab.items.len));
elf_file.strtab.appendSliceAssumeCapacity(global.name(elf_file));
elf_file.strtab.appendAssumeCapacity(0);
if (global.isLocal(elf_file)) {
const out_sym = &elf_file.symtab.items[ilocal];
out_sym.st_name = st_name;
global.setOutputSym(elf_file, out_sym);
ilocal += 1;
} else {
const out_sym = &elf_file.symtab.items[iglobal];
out_sym.st_name = st_name;
global.setOutputSym(elf_file, out_sym);
iglobal += 1;
}
}
}
pub fn updateArSymtab(file: File, ar_symtab: *Archive.ArSymtab, elf_file: *Elf) !void {
return switch (file) {
.zig_object => |x| x.updateArSymtab(ar_symtab, elf_file),
.object => @panic("TODO"),
inline else => unreachable,
};
}
pub const Index = u32;
pub const Entry = union(enum) {
null: void,
zig_object: ZigObject,
linker_defined: LinkerDefined,
object: Object,
shared_object: SharedObject,
};
};
const std = @import("std");
const elf = std.elf;
const Allocator = std.mem.Allocator;
const Archive = @import("Archive.zig");
const Atom = @import("Atom.zig");
const Cie = @import("eh_frame.zig").Cie;
const Elf = @import("../Elf.zig");
const LinkerDefined = @import("LinkerDefined.zig");
const Object = @import("Object.zig");
const SharedObject = @import("SharedObject.zig");
const Symbol = @import("Symbol.zig");
const ZigObject = @import("ZigObject.zig");