Merge pull request #20873 from ziglang/elf-ownership

elf: clean up resource ownership in the linker
This commit is contained in:
Jakub Konka
2024-07-30 18:49:39 +02:00
committed by GitHub
17 changed files with 1205 additions and 1008 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -30,14 +30,17 @@ atom_index: Index = 0,
prev_index: Index = 0,
next_index: Index = 0,
/// Flags we use for state tracking.
flags: Flags = .{},
/// Specifies whether this atom is alive or has been garbage collected.
alive: bool = true,
/// Specifies if the atom has been visited during garbage collection.
visited: bool = false,
extra_index: u32 = 0,
pub const Alignment = @import("../../InternPool.zig").Alignment;
pub fn name(self: Atom, elf_file: *Elf) []const u8 {
pub fn name(self: Atom, elf_file: *Elf) [:0]const u8 {
const file_ptr = self.file(elf_file).?;
return switch (file_ptr) {
inline else => |x| x.getString(self.name_offset),
@@ -45,8 +48,7 @@ pub fn name(self: Atom, elf_file: *Elf) []const u8 {
}
pub fn address(self: Atom, elf_file: *Elf) i64 {
const shndx = self.outputShndx() orelse return self.value;
const shdr = elf_file.shdrs.items[shndx];
const shdr = elf_file.shdrs.items[self.output_section_index];
return @as(i64, @intCast(shdr.sh_addr)) + self.value;
}
@@ -55,7 +57,7 @@ pub fn debugTombstoneValue(self: Atom, target: Symbol, elf_file: *Elf) ?u64 {
if (msub.alive) return null;
}
if (target.atom(elf_file)) |atom_ptr| {
if (atom_ptr.flags.alive) return null;
if (atom_ptr.alive) return null;
}
const atom_name = self.name(elf_file);
if (!mem.startsWith(u8, atom_name, ".debug")) return null;
@@ -67,8 +69,7 @@ pub fn file(self: Atom, elf_file: *Elf) ?File {
}
pub fn thunk(self: Atom, elf_file: *Elf) *Thunk {
assert(self.flags.thunk);
const extras = self.extra(elf_file).?;
const extras = self.extra(elf_file);
return elf_file.thunk(extras.thunk);
}
@@ -85,11 +86,6 @@ pub fn relocsShndx(self: Atom) ?u32 {
return self.relocs_section_index;
}
pub fn outputShndx(self: Atom) ?u32 {
if (self.output_section_index == 0) return null;
return self.output_section_index;
}
pub fn priority(self: Atom, elf_file: *Elf) u64 {
const index = self.file(elf_file).?.index();
return (@as(u64, @intCast(index)) << 32) | @as(u64, @intCast(self.input_section_index));
@@ -99,7 +95,8 @@ pub fn priority(self: Atom, elf_file: *Elf) u64 {
/// File offset relocation happens transparently, so it is not included in
/// this calculation.
pub fn capacity(self: Atom, elf_file: *Elf) u64 {
const next_addr = if (elf_file.atom(self.next_index)) |next|
const zo = elf_file.zigObjectPtr().?;
const next_addr = if (zo.atom(self.next_index)) |next|
next.address(elf_file)
else
std.math.maxInt(u32);
@@ -107,8 +104,9 @@ pub fn capacity(self: Atom, elf_file: *Elf) u64 {
}
pub fn freeListEligible(self: Atom, elf_file: *Elf) bool {
const zo = elf_file.zigObjectPtr().?;
// No need to keep a free list node for the last block.
const next = elf_file.atom(self.next_index) orelse return false;
const next = zo.atom(self.next_index) orelse return false;
const cap: u64 = @intCast(next.address(elf_file) - self.address(elf_file));
const ideal_cap = Elf.padToIdeal(self.size);
if (cap <= ideal_cap) return false;
@@ -117,8 +115,9 @@ pub fn freeListEligible(self: Atom, elf_file: *Elf) bool {
}
pub fn allocate(self: *Atom, elf_file: *Elf) !void {
const shdr = &elf_file.shdrs.items[self.outputShndx().?];
const meta = elf_file.last_atom_and_free_list_table.getPtr(self.outputShndx().?).?;
const zo = elf_file.zigObjectPtr().?;
const shdr = &elf_file.shdrs.items[self.output_section_index];
const meta = elf_file.last_atom_and_free_list_table.getPtr(self.output_section_index).?;
const free_list = &meta.free_list;
const last_atom_index = &meta.last_atom_index;
const new_atom_ideal_capacity = Elf.padToIdeal(self.size);
@@ -137,7 +136,7 @@ pub fn allocate(self: *Atom, elf_file: *Elf) !void {
var i: usize = if (elf_file.base.child_pid == null) 0 else free_list.items.len;
while (i < free_list.items.len) {
const big_atom_index = free_list.items[i];
const big_atom = elf_file.atom(big_atom_index).?;
const big_atom = zo.atom(big_atom_index).?;
// We now have a pointer to a live atom that has too much capacity.
// Is it enough that we could fit this new atom?
const cap = big_atom.capacity(elf_file);
@@ -169,7 +168,7 @@ pub fn allocate(self: *Atom, elf_file: *Elf) !void {
free_list_removal = i;
}
break :blk @intCast(new_start_vaddr);
} else if (elf_file.atom(last_atom_index.*)) |last| {
} else if (zo.atom(last_atom_index.*)) |last| {
const ideal_capacity = Elf.padToIdeal(last.size);
const ideal_capacity_end_vaddr = @as(u64, @intCast(last.value)) + ideal_capacity;
const new_start_vaddr = self.alignment.forward(ideal_capacity_end_vaddr);
@@ -189,12 +188,12 @@ pub fn allocate(self: *Atom, elf_file: *Elf) !void {
});
const expand_section = if (atom_placement) |placement_index|
elf_file.atom(placement_index).?.next_index == 0
zo.atom(placement_index).?.next_index == 0
else
true;
if (expand_section) {
const needed_size: u64 = @intCast(self.value + @as(i64, @intCast(self.size)));
try elf_file.growAllocSection(self.outputShndx().?, needed_size);
try elf_file.growAllocSection(self.output_section_index, needed_size);
last_atom_index.* = self.atom_index;
const zig_object = elf_file.zigObjectPtr().?;
@@ -214,15 +213,15 @@ pub fn allocate(self: *Atom, elf_file: *Elf) !void {
// This function can also reallocate an atom.
// In this case we need to "unplug" it from its previous location before
// plugging it in to its new location.
if (elf_file.atom(self.prev_index)) |prev| {
if (zo.atom(self.prev_index)) |prev| {
prev.next_index = self.next_index;
}
if (elf_file.atom(self.next_index)) |next| {
if (zo.atom(self.next_index)) |next| {
next.prev_index = self.prev_index;
}
if (atom_placement) |big_atom_index| {
const big_atom = elf_file.atom(big_atom_index).?;
const big_atom = zo.atom(big_atom_index).?;
self.prev_index = big_atom_index;
self.next_index = big_atom.next_index;
big_atom.next_index = self.atom_index;
@@ -234,7 +233,7 @@ pub fn allocate(self: *Atom, elf_file: *Elf) !void {
_ = free_list.swapRemove(i);
}
self.flags.alive = true;
self.alive = true;
}
pub fn shrink(self: *Atom, elf_file: *Elf) void {
@@ -250,9 +249,10 @@ pub fn grow(self: *Atom, elf_file: *Elf) !void {
pub fn free(self: *Atom, elf_file: *Elf) void {
log.debug("freeAtom {d} ({s})", .{ self.atom_index, self.name(elf_file) });
const zo = elf_file.zigObjectPtr().?;
const comp = elf_file.base.comp;
const gpa = comp.gpa;
const shndx = self.outputShndx().?;
const shndx = self.output_section_index;
const meta = elf_file.last_atom_and_free_list_table.getPtr(shndx).?;
const free_list = &meta.free_list;
const last_atom_index = &meta.last_atom_index;
@@ -272,9 +272,9 @@ pub fn free(self: *Atom, elf_file: *Elf) void {
}
}
if (elf_file.atom(last_atom_index.*)) |last_atom| {
if (zo.atom(last_atom_index.*)) |last_atom| {
if (last_atom.atom_index == self.atom_index) {
if (elf_file.atom(self.prev_index)) |_| {
if (zo.atom(self.prev_index)) |_| {
// TODO shrink the section size here
last_atom_index.* = self.prev_index;
} else {
@@ -283,7 +283,7 @@ pub fn free(self: *Atom, elf_file: *Elf) void {
}
}
if (elf_file.atom(self.prev_index)) |prev| {
if (zo.atom(self.prev_index)) |prev| {
prev.next_index = self.next_index;
if (!already_have_free_list_node and prev.*.freeListEligible(elf_file)) {
// The free list is heuristics, it doesn't have to be perfect, so we can
@@ -294,7 +294,7 @@ pub fn free(self: *Atom, elf_file: *Elf) void {
self.prev_index = 0;
}
if (elf_file.atom(self.next_index)) |next| {
if (zo.atom(self.next_index)) |next| {
next.prev_index = self.prev_index;
} else {
self.next_index = 0;
@@ -313,7 +313,7 @@ pub fn relocs(self: Atom, elf_file: *Elf) []const elf.Elf64_Rela {
switch (self.file(elf_file).?) {
.zig_object => |x| return x.relocs.items[shndx].items,
.object => |x| {
const extras = self.extra(elf_file).?;
const extras = self.extra(elf_file);
return x.relocs.items[extras.rel_index..][0..extras.rel_count];
},
else => unreachable,
@@ -337,12 +337,12 @@ pub fn writeRelocs(self: Atom, elf_file: *Elf, out_relocs: *std.ArrayList(elf.El
var r_addend = rel.r_addend;
var r_sym: u32 = 0;
switch (target.type(elf_file)) {
elf.STT_SECTION => if (target.mergeSubsection(elf_file)) |msub| {
elf.STT_SECTION => {
r_addend += @intCast(target.address(.{}, elf_file));
r_sym = elf_file.sectionSymbolOutputSymtabIndex(msub.mergeSection(elf_file).output_section_index);
} else {
r_addend += @intCast(target.address(.{}, elf_file));
r_sym = elf_file.sectionSymbolOutputSymtabIndex(target.outputShndx().?);
r_sym = if (target.outputShndx(elf_file)) |osec|
elf_file.sectionSymbolOutputSymtabIndex(osec)
else
0;
},
else => {
r_sym = target.outputSymtabIndex(elf_file) orelse 0;
@@ -366,10 +366,12 @@ pub fn writeRelocs(self: Atom, elf_file: *Elf, out_relocs: *std.ArrayList(elf.El
}
pub fn fdes(self: Atom, elf_file: *Elf) []Fde {
if (!self.flags.fde) return &[0]Fde{};
const extras = self.extra(elf_file).?;
const object = self.file(elf_file).?.object;
return object.fdes.items[extras.fde_start..][0..extras.fde_count];
const extras = self.extra(elf_file);
return switch (self.file(elf_file).?) {
.shared_object => unreachable,
.linker_defined, .zig_object => &[0]Fde{},
.object => |x| x.fdes.items[extras.fde_start..][0..extras.fde_count],
};
}
pub fn markFdesDead(self: Atom, elf_file: *Elf) void {
@@ -712,9 +714,9 @@ fn reportUndefined(
{
const gop = try undefs.getOrPut(sym_index);
if (!gop.found_existing) {
gop.value_ptr.* = std.ArrayList(Atom.Index).init(gpa);
gop.value_ptr.* = std.ArrayList(Elf.Ref).init(gpa);
}
try gop.value_ptr.append(self.atom_index);
try gop.value_ptr.append(.{ .index = self.atom_index, .file = self.file_index });
return true;
}
@@ -1001,25 +1003,23 @@ const AddExtraOpts = struct {
rel_count: ?u32 = null,
};
pub fn addExtra(atom: *Atom, opts: AddExtraOpts, elf_file: *Elf) !void {
if (atom.extra(elf_file) == null) {
atom.extra_index = try elf_file.addAtomExtra(.{});
}
var extras = atom.extra(elf_file).?;
pub fn addExtra(atom: *Atom, opts: AddExtraOpts, elf_file: *Elf) void {
const file_ptr = atom.file(elf_file).?;
var extras = file_ptr.atomExtra(atom.extra_index);
inline for (@typeInfo(@TypeOf(opts)).Struct.fields) |field| {
if (@field(opts, field.name)) |x| {
@field(extras, field.name) = x;
}
}
atom.setExtra(extras, elf_file);
file_ptr.setAtomExtra(atom.extra_index, extras);
}
pub inline fn extra(atom: Atom, elf_file: *Elf) ?Extra {
return elf_file.atomExtra(atom.extra_index);
pub inline fn extra(atom: Atom, elf_file: *Elf) Extra {
return atom.file(elf_file).?.atomExtra(atom.extra_index);
}
pub inline fn setExtra(atom: Atom, extras: Extra, elf_file: *Elf) void {
elf_file.setAtomExtra(atom.extra_index, extras);
atom.file(elf_file).?.setAtomExtra(atom.extra_index, extras);
}
pub fn format(
@@ -1061,9 +1061,9 @@ fn format2(
atom.atom_index, atom.name(elf_file), atom.address(elf_file),
atom.output_section_index, atom.alignment, atom.size,
});
if (atom.flags.fde) {
if (atom.fdes(elf_file).len > 0) {
try writer.writeAll(" : fdes{ ");
const extras = atom.extra(elf_file).?;
const extras = atom.extra(elf_file);
for (atom.fdes(elf_file), extras.fde_start..) |fde, i| {
try writer.print("{d}", .{i});
if (!fde.alive) try writer.writeAll("([*])");
@@ -1071,27 +1071,13 @@ fn format2(
}
try writer.writeAll(" }");
}
if (!atom.flags.alive) {
if (!atom.alive) {
try writer.writeAll(" : [*]");
}
}
pub const Index = u32;
pub const Flags = packed struct {
/// Specifies whether this atom is alive or has been garbage collected.
alive: bool = true,
/// Specifies if the atom has been visited during garbage collection.
visited: bool = false,
/// Whether this atom has a range extension thunk.
thunk: bool = false,
/// Whether this atom has FDE records.
fde: bool = false,
};
const x86_64 = struct {
fn scanReloc(
atom: Atom,

View File

@@ -1,17 +1,89 @@
index: File.Index,
symtab: std.ArrayListUnmanaged(elf.Elf64_Sym) = .{},
strtab: std.ArrayListUnmanaged(u8) = .{},
symbols: std.ArrayListUnmanaged(Symbol.Index) = .{},
entry_index: ?Symbol.Index = null,
dynamic_index: ?Symbol.Index = null,
ehdr_start_index: ?Symbol.Index = null,
init_array_start_index: ?Symbol.Index = null,
init_array_end_index: ?Symbol.Index = null,
fini_array_start_index: ?Symbol.Index = null,
fini_array_end_index: ?Symbol.Index = null,
preinit_array_start_index: ?Symbol.Index = null,
preinit_array_end_index: ?Symbol.Index = null,
got_index: ?Symbol.Index = null,
plt_index: ?Symbol.Index = null,
end_index: ?Symbol.Index = null,
gnu_eh_frame_hdr_index: ?Symbol.Index = null,
dso_handle_index: ?Symbol.Index = null,
rela_iplt_start_index: ?Symbol.Index = null,
rela_iplt_end_index: ?Symbol.Index = null,
global_pointer_index: ?Symbol.Index = null,
start_stop_indexes: std.ArrayListUnmanaged(u32) = .{},
output_symtab_ctx: Elf.SymtabCtx = .{},
pub fn deinit(self: *LinkerDefined, allocator: Allocator) void {
self.symtab.deinit(allocator);
self.strtab.deinit(allocator);
self.symbols.deinit(allocator);
self.start_stop_indexes.deinit(allocator);
}
pub fn addGlobal(self: *LinkerDefined, name: [:0]const u8, elf_file: *Elf) !u32 {
pub fn init(self: *LinkerDefined, allocator: Allocator) !void {
// Null byte in strtab
try self.strtab.append(allocator, 0);
}
pub fn initSymbols(self: *LinkerDefined, elf_file: *Elf) !void {
const gpa = elf_file.base.comp.gpa;
if (elf_file.entry_name) |name| {
self.entry_index = try self.addGlobal(name, elf_file);
}
self.dynamic_index = try self.addGlobal("_DYNAMIC", elf_file);
self.ehdr_start_index = try self.addGlobal("__ehdr_start", elf_file);
self.init_array_start_index = try self.addGlobal("__init_array_start", elf_file);
self.init_array_end_index = try self.addGlobal("__init_array_end", elf_file);
self.fini_array_start_index = try self.addGlobal("__fini_array_start", elf_file);
self.fini_array_end_index = try self.addGlobal("__fini_array_end", elf_file);
self.preinit_array_start_index = try self.addGlobal("__preinit_array_start", elf_file);
self.preinit_array_end_index = try self.addGlobal("__preinit_array_end", elf_file);
self.got_index = try self.addGlobal("_GLOBAL_OFFSET_TABLE_", elf_file);
self.plt_index = try self.addGlobal("_PROCEDURE_LINKAGE_TABLE_", elf_file);
self.end_index = try self.addGlobal("_end", elf_file);
if (elf_file.base.comp.link_eh_frame_hdr) {
self.gnu_eh_frame_hdr_index = try self.addGlobal("__GNU_EH_FRAME_HDR", elf_file);
}
self.dso_handle_index = try self.addGlobal("__dso_handle", elf_file);
self.rela_iplt_start_index = try self.addGlobal("__rela_iplt_start", elf_file);
self.rela_iplt_end_index = try self.addGlobal("__rela_iplt_end", elf_file);
for (elf_file.shdrs.items) |shdr| {
if (elf_file.getStartStopBasename(shdr)) |name| {
try self.start_stop_indexes.ensureUnusedCapacity(gpa, 2);
const start = try std.fmt.allocPrintZ(gpa, "__start_{s}", .{name});
defer gpa.free(start);
const stop = try std.fmt.allocPrintZ(gpa, "__stop_{s}", .{name});
defer gpa.free(stop);
self.start_stop_indexes.appendAssumeCapacity(try self.addGlobal(start, elf_file));
self.start_stop_indexes.appendAssumeCapacity(try self.addGlobal(stop, elf_file));
}
}
if (elf_file.getTarget().cpu.arch.isRISCV() and elf_file.isEffectivelyDynLib()) {
self.global_pointer_index = try self.addGlobal("__global_pointer$", elf_file);
}
}
fn addGlobal(self: *LinkerDefined, name: []const u8, elf_file: *Elf) !u32 {
const comp = elf_file.base.comp;
const gpa = comp.gpa;
try self.symtab.ensureUnusedCapacity(gpa, 1);
@@ -41,7 +113,7 @@ pub fn resolveSymbols(self: *LinkerDefined, elf_file: *Elf) void {
const global = elf_file.symbol(index);
if (self.asFile().symbolRank(this_sym, false) < global.symbolRank(elf_file)) {
global.value = 0;
global.atom_index = 0;
global.ref = .{ .index = 0, .file = 0 };
global.file_index = self.index;
global.esym_index = sym_idx;
global.version_index = elf_file.default_sym_version;
@@ -49,6 +121,154 @@ pub fn resolveSymbols(self: *LinkerDefined, elf_file: *Elf) void {
}
}
pub fn allocateSymbols(self: *LinkerDefined, elf_file: *Elf) void {
const comp = elf_file.base.comp;
const link_mode = comp.config.link_mode;
// _DYNAMIC
if (elf_file.dynamic_section_index) |shndx| {
const shdr = &elf_file.shdrs.items[shndx];
const symbol_ptr = elf_file.symbol(self.dynamic_index.?);
symbol_ptr.value = @intCast(shdr.sh_addr);
symbol_ptr.output_section_index = shndx;
}
// __ehdr_start
{
const symbol_ptr = elf_file.symbol(self.ehdr_start_index.?);
symbol_ptr.value = @intCast(elf_file.image_base);
symbol_ptr.output_section_index = 1;
}
// __init_array_start, __init_array_end
if (elf_file.sectionByName(".init_array")) |shndx| {
const start_sym = elf_file.symbol(self.init_array_start_index.?);
const end_sym = elf_file.symbol(self.init_array_end_index.?);
const shdr = &elf_file.shdrs.items[shndx];
start_sym.output_section_index = shndx;
start_sym.value = @intCast(shdr.sh_addr);
end_sym.output_section_index = shndx;
end_sym.value = @intCast(shdr.sh_addr + shdr.sh_size);
}
// __fini_array_start, __fini_array_end
if (elf_file.sectionByName(".fini_array")) |shndx| {
const start_sym = elf_file.symbol(self.fini_array_start_index.?);
const end_sym = elf_file.symbol(self.fini_array_end_index.?);
const shdr = &elf_file.shdrs.items[shndx];
start_sym.output_section_index = shndx;
start_sym.value = @intCast(shdr.sh_addr);
end_sym.output_section_index = shndx;
end_sym.value = @intCast(shdr.sh_addr + shdr.sh_size);
}
// __preinit_array_start, __preinit_array_end
if (elf_file.sectionByName(".preinit_array")) |shndx| {
const start_sym = elf_file.symbol(self.preinit_array_start_index.?);
const end_sym = elf_file.symbol(self.preinit_array_end_index.?);
const shdr = &elf_file.shdrs.items[shndx];
start_sym.output_section_index = shndx;
start_sym.value = @intCast(shdr.sh_addr);
end_sym.output_section_index = shndx;
end_sym.value = @intCast(shdr.sh_addr + shdr.sh_size);
}
// _GLOBAL_OFFSET_TABLE_
if (elf_file.getTarget().cpu.arch == .x86_64) {
if (elf_file.got_plt_section_index) |shndx| {
const shdr = elf_file.shdrs.items[shndx];
const sym = elf_file.symbol(self.got_index.?);
sym.value = @intCast(shdr.sh_addr);
sym.output_section_index = shndx;
}
} else {
if (elf_file.got_section_index) |shndx| {
const shdr = elf_file.shdrs.items[shndx];
const sym = elf_file.symbol(self.got_index.?);
sym.value = @intCast(shdr.sh_addr);
sym.output_section_index = shndx;
}
}
// _PROCEDURE_LINKAGE_TABLE_
if (elf_file.plt_section_index) |shndx| {
const shdr = &elf_file.shdrs.items[shndx];
const symbol_ptr = elf_file.symbol(self.plt_index.?);
symbol_ptr.value = @intCast(shdr.sh_addr);
symbol_ptr.output_section_index = shndx;
}
// __dso_handle
if (self.dso_handle_index) |index| {
const shdr = &elf_file.shdrs.items[1];
const symbol_ptr = elf_file.symbol(index);
symbol_ptr.value = @intCast(shdr.sh_addr);
symbol_ptr.output_section_index = 0;
}
// __GNU_EH_FRAME_HDR
if (elf_file.eh_frame_hdr_section_index) |shndx| {
const shdr = &elf_file.shdrs.items[shndx];
const symbol_ptr = elf_file.symbol(self.gnu_eh_frame_hdr_index.?);
symbol_ptr.value = @intCast(shdr.sh_addr);
symbol_ptr.output_section_index = shndx;
}
// __rela_iplt_start, __rela_iplt_end
if (elf_file.rela_dyn_section_index) |shndx| blk: {
if (link_mode != .static or comp.config.pie) break :blk;
const shdr = &elf_file.shdrs.items[shndx];
const end_addr = shdr.sh_addr + shdr.sh_size;
const start_addr = end_addr - elf_file.calcNumIRelativeRelocs() * @sizeOf(elf.Elf64_Rela);
const start_sym = elf_file.symbol(self.rela_iplt_start_index.?);
const end_sym = elf_file.symbol(self.rela_iplt_end_index.?);
start_sym.value = @intCast(start_addr);
start_sym.output_section_index = shndx;
end_sym.value = @intCast(end_addr);
end_sym.output_section_index = shndx;
}
// _end
{
const end_symbol = elf_file.symbol(self.end_index.?);
for (elf_file.shdrs.items, 0..) |shdr, shndx| {
if (shdr.sh_flags & elf.SHF_ALLOC != 0) {
end_symbol.value = @intCast(shdr.sh_addr + shdr.sh_size);
end_symbol.output_section_index = @intCast(shndx);
}
}
}
// __start_*, __stop_*
{
var index: usize = 0;
while (index < self.start_stop_indexes.items.len) : (index += 2) {
const start = elf_file.symbol(self.start_stop_indexes.items[index]);
const name = start.name(elf_file);
const stop = elf_file.symbol(self.start_stop_indexes.items[index + 1]);
const shndx = elf_file.sectionByName(name["__start_".len..]).?;
const shdr = &elf_file.shdrs.items[shndx];
start.value = @intCast(shdr.sh_addr);
start.output_section_index = shndx;
stop.value = @intCast(shdr.sh_addr + shdr.sh_size);
stop.output_section_index = shndx;
}
}
// __global_pointer$
if (self.global_pointer_index) |index| {
const sym = elf_file.symbol(index);
if (elf_file.sectionByName(".sdata")) |shndx| {
const shdr = elf_file.shdrs.items[shndx];
sym.value = @intCast(shdr.sh_addr + 0x800);
sym.output_section_index = shndx;
} else {
sym.value = 0;
sym.output_section_index = 0;
}
}
}
pub fn globals(self: LinkerDefined) []const Symbol.Index {
return self.symbols.items;
}
@@ -127,6 +347,7 @@ const mem = std.mem;
const std = @import("std");
const Allocator = mem.Allocator;
const Atom = @import("Atom.zig");
const Elf = @import("../Elf.zig");
const File = @import("file.zig").File;
const LinkerDefined = @This();

View File

@@ -10,12 +10,17 @@ symtab: std.ArrayListUnmanaged(elf.Elf64_Sym) = .{},
strtab: std.ArrayListUnmanaged(u8) = .{},
first_global: ?Symbol.Index = null,
symbols: std.ArrayListUnmanaged(Symbol.Index) = .{},
atoms: std.ArrayListUnmanaged(Atom.Index) = .{},
comdat_groups: std.ArrayListUnmanaged(Elf.ComdatGroup.Index) = .{},
comdat_group_data: std.ArrayListUnmanaged(u32) = .{},
relocs: std.ArrayListUnmanaged(elf.Elf64_Rela) = .{},
merge_sections: std.ArrayListUnmanaged(InputMergeSection.Index) = .{},
atoms: std.ArrayListUnmanaged(Atom) = .{},
atoms_indexes: std.ArrayListUnmanaged(Atom.Index) = .{},
atoms_extra: std.ArrayListUnmanaged(u32) = .{},
comdat_groups: std.ArrayListUnmanaged(Elf.ComdatGroup) = .{},
comdat_group_data: std.ArrayListUnmanaged(u32) = .{},
input_merge_sections: std.ArrayListUnmanaged(InputMergeSection) = .{},
input_merge_sections_indexes: std.ArrayListUnmanaged(InputMergeSection.Index) = .{},
fdes: std.ArrayListUnmanaged(Fde) = .{},
cies: std.ArrayListUnmanaged(Cie) = .{},
@@ -47,13 +52,19 @@ pub fn deinit(self: *Object, allocator: Allocator) void {
self.strtab.deinit(allocator);
self.symbols.deinit(allocator);
self.atoms.deinit(allocator);
self.atoms_indexes.deinit(allocator);
self.atoms_extra.deinit(allocator);
self.comdat_groups.deinit(allocator);
self.comdat_group_data.deinit(allocator);
self.relocs.deinit(allocator);
self.fdes.deinit(allocator);
self.cies.deinit(allocator);
self.eh_frame_data.deinit(allocator);
self.merge_sections.deinit(allocator);
for (self.input_merge_sections.items) |*isec| {
isec.deinit(allocator);
}
self.input_merge_sections.deinit(allocator);
self.input_merge_sections_indexes.deinit(allocator);
}
pub fn parse(self: *Object, elf_file: *Elf) !void {
@@ -62,14 +73,20 @@ pub fn parse(self: *Object, elf_file: *Elf) !void {
const handle = elf_file.fileHandle(self.file_handle);
try self.parseCommon(gpa, handle, elf_file);
// Append null input merge section
try self.input_merge_sections.append(gpa, .{});
// Allocate atom index 0 to null atom
try self.atoms.append(gpa, .{ .extra_index = try self.addAtomExtra(gpa, .{}) });
try self.initAtoms(gpa, handle, elf_file);
try self.initSymtab(gpa, elf_file);
for (self.shdrs.items, 0..) |shdr, i| {
const atom = elf_file.atom(self.atoms.items[i]) orelse continue;
if (!atom.flags.alive) continue;
const atom_ptr = self.atom(self.atoms_indexes.items[i]) orelse continue;
if (!atom_ptr.alive) continue;
if ((cpu_arch == .x86_64 and shdr.sh_type == elf.SHT_X86_64_UNWIND) or
mem.eql(u8, atom.name(elf_file), ".eh_frame"))
mem.eql(u8, atom_ptr.name(elf_file), ".eh_frame"))
{
try self.parseEhFrame(gpa, handle, @as(u32, @intCast(i)), elf_file);
}
@@ -169,8 +186,11 @@ fn parseCommon(self: *Object, allocator: Allocator, handle: std.fs.File, elf_fil
fn initAtoms(self: *Object, allocator: Allocator, handle: std.fs.File, elf_file: *Elf) !void {
const shdrs = self.shdrs.items;
try self.atoms.resize(allocator, shdrs.len);
@memset(self.atoms.items, 0);
try self.atoms.ensureTotalCapacityPrecise(allocator, shdrs.len);
try self.atoms_extra.ensureTotalCapacityPrecise(allocator, shdrs.len * @sizeOf(Atom.Extra));
try self.atoms_indexes.ensureTotalCapacityPrecise(allocator, shdrs.len);
try self.atoms_indexes.resize(allocator, shdrs.len);
@memset(self.atoms_indexes.items, 0);
for (shdrs, 0..) |shdr, i| {
if (shdr.sh_flags & elf.SHF_EXCLUDE != 0 and
@@ -188,37 +208,53 @@ fn initAtoms(self: *Object, allocator: Allocator, handle: std.fs.File, elf_file:
const group_signature = blk: {
if (group_info_sym.st_name == 0 and group_info_sym.st_type() == elf.STT_SECTION) {
const sym_shdr = shdrs[group_info_sym.st_shndx];
break :blk self.getString(sym_shdr.sh_name);
break :blk sym_shdr.sh_name;
}
break :blk self.getString(group_info_sym.st_name);
break :blk group_info_sym.st_name;
};
const shndx = @as(u32, @intCast(i));
const group_raw_data = try self.preadShdrContentsAlloc(allocator, handle, shndx);
defer allocator.free(group_raw_data);
const group_nmembers = @divExact(group_raw_data.len, @sizeOf(u32));
const group_nmembers = math.divExact(usize, group_raw_data.len, @sizeOf(u32)) catch {
try elf_file.reportParseError2(
self.index,
"corrupt section group: not evenly divisible ",
.{},
);
return error.MalformedObject;
};
if (group_nmembers == 0) {
try elf_file.reportParseError2(
self.index,
"corrupt section group: empty section",
.{},
);
return error.MalformedObject;
}
const group_members = @as([*]align(1) const u32, @ptrCast(group_raw_data.ptr))[0..group_nmembers];
if (group_members[0] != elf.GRP_COMDAT) {
// TODO convert into an error
log.debug("{}: unknown SHT_GROUP format", .{self.fmtPath()});
continue;
try elf_file.reportParseError2(
self.index,
"corrupt section group: unknown SHT_GROUP format",
.{},
);
return error.MalformedObject;
}
const group_start = @as(u32, @intCast(self.comdat_group_data.items.len));
try self.comdat_group_data.appendUnalignedSlice(allocator, group_members[1..]);
const gop = try elf_file.getOrCreateComdatGroupOwner(group_signature);
const comdat_group_index = try elf_file.addComdatGroup();
const comdat_group = elf_file.comdatGroup(comdat_group_index);
const comdat_group_index = try self.addComdatGroup(allocator);
const comdat_group = self.comdatGroup(comdat_group_index);
comdat_group.* = .{
.owner = gop.index,
.file = self.index,
.signature_off = group_signature,
.file_index = self.index,
.shndx = shndx,
.members_start = group_start,
.members_len = @intCast(group_nmembers - 1),
};
try self.comdat_groups.append(allocator, comdat_group_index);
},
elf.SHT_SYMTAB_SHNDX => @panic("TODO SHT_SYMTAB_SHNDX"),
@@ -233,7 +269,19 @@ fn initAtoms(self: *Object, allocator: Allocator, handle: std.fs.File, elf_file:
else => {
const shndx = @as(u32, @intCast(i));
if (self.skipShdr(shndx, elf_file)) continue;
try self.addAtom(allocator, handle, shdr, shndx, elf_file);
const size, const alignment = if (shdr.sh_flags & elf.SHF_COMPRESSED != 0) blk: {
const data = try self.preadShdrContentsAlloc(allocator, handle, shndx);
defer allocator.free(data);
const chdr = @as(*align(1) const elf.Elf64_Chdr, @ptrCast(data.ptr)).*;
break :blk .{ chdr.ch_size, Alignment.fromNonzeroByteUnits(chdr.ch_addralign) };
} else .{ shdr.sh_size, Alignment.fromNonzeroByteUnits(shdr.sh_addralign) };
const atom_index = self.addAtomAssumeCapacity(.{
.name = shdr.sh_name,
.shndx = shndx,
.size = size,
.alignment = alignment,
});
self.atoms_indexes.items[shndx] = atom_index;
},
}
}
@@ -241,14 +289,14 @@ fn initAtoms(self: *Object, allocator: Allocator, handle: std.fs.File, elf_file:
// Parse relocs sections if any.
for (shdrs, 0..) |shdr, i| switch (shdr.sh_type) {
elf.SHT_REL, elf.SHT_RELA => {
const atom_index = self.atoms.items[shdr.sh_info];
if (elf_file.atom(atom_index)) |atom| {
const atom_index = self.atoms_indexes.items[shdr.sh_info];
if (self.atom(atom_index)) |atom_ptr| {
const relocs = try self.preadRelocsAlloc(allocator, handle, @intCast(i));
defer allocator.free(relocs);
atom.relocs_section_index = @intCast(i);
atom_ptr.relocs_section_index = @intCast(i);
const rel_index: u32 = @intCast(self.relocs.items.len);
const rel_count: u32 = @intCast(relocs.len);
try atom.addExtra(.{ .rel_index = rel_index, .rel_count = rel_count }, elf_file);
atom_ptr.addExtra(.{ .rel_index = rel_index, .rel_count = rel_count }, elf_file);
try self.relocs.appendUnalignedSlice(allocator, relocs);
if (elf_file.getTarget().cpu.arch == .riscv64) {
sortRelocs(self.relocs.items[rel_index..][0..rel_count]);
@@ -259,27 +307,6 @@ fn initAtoms(self: *Object, allocator: Allocator, handle: std.fs.File, elf_file:
};
}
fn addAtom(self: *Object, allocator: Allocator, handle: std.fs.File, shdr: elf.Elf64_Shdr, shndx: u32, elf_file: *Elf) !void {
const atom_index = try elf_file.addAtom();
const atom = elf_file.atom(atom_index).?;
atom.atom_index = atom_index;
atom.name_offset = shdr.sh_name;
atom.file_index = self.index;
atom.input_section_index = shndx;
self.atoms.items[shndx] = atom_index;
if (shdr.sh_flags & elf.SHF_COMPRESSED != 0) {
const data = try self.preadShdrContentsAlloc(allocator, handle, shndx);
defer allocator.free(data);
const chdr = @as(*align(1) const elf.Elf64_Chdr, @ptrCast(data.ptr)).*;
atom.size = chdr.ch_size;
atom.alignment = Alignment.fromNonzeroByteUnits(chdr.ch_addralign);
} else {
atom.size = shdr.sh_size;
atom.alignment = Alignment.fromNonzeroByteUnits(shdr.sh_addralign);
}
}
fn initOutputSection(self: Object, elf_file: *Elf, shdr: elf.Elf64_Shdr) error{OutOfMemory}!u32 {
const name = blk: {
const name = self.getString(shdr.sh_name);
@@ -327,7 +354,7 @@ fn initOutputSection(self: Object, elf_file: *Elf, shdr: elf.Elf64_Shdr) error{O
const out_shndx = elf_file.sectionByName(name) orelse try elf_file.addSection(.{
.type = @"type",
.flags = flags,
.name = name,
.name = try elf_file.insertShString(name),
});
return out_shndx;
}
@@ -359,8 +386,10 @@ fn initSymtab(self: *Object, allocator: Allocator, elf_file: *Elf) !void {
sym_ptr.value = @intCast(sym.st_value);
sym_ptr.name_offset = sym.st_name;
sym_ptr.esym_index = @as(u32, @intCast(i));
sym_ptr.atom_index = if (sym.st_shndx == elf.SHN_ABS) 0 else self.atoms.items[sym.st_shndx];
sym_ptr.file_index = self.index;
if (sym.st_shndx != elf.SHN_ABS) {
sym_ptr.ref = .{ .index = self.atoms_indexes.items[sym.st_shndx], .file = self.index };
}
}
for (self.symtab.items[first_global..]) |sym| {
@@ -447,15 +476,14 @@ fn parseEhFrame(self: *Object, allocator: Allocator, handle: std.fs.File, shndx:
var i: u32 = @as(u32, @intCast(fdes_start));
while (i < self.fdes.items.len) {
const fde = self.fdes.items[i];
const atom = fde.atom(elf_file);
const atom_ptr = fde.atom(elf_file);
const start = i;
i += 1;
while (i < self.fdes.items.len) : (i += 1) {
const next_fde = self.fdes.items[i];
if (atom.atom_index != next_fde.atom(elf_file).atom_index) break;
if (atom_ptr.atom_index != next_fde.atom(elf_file).atom_index) break;
}
try atom.addExtra(.{ .fde_start = start, .fde_count = i - start }, elf_file);
atom.flags.fde = true;
atom_ptr.addExtra(.{ .fde_start = start, .fde_count = i - start }, elf_file);
}
}
@@ -498,19 +526,19 @@ fn filterRelocs(
pub fn scanRelocs(self: *Object, elf_file: *Elf, undefs: anytype) !void {
const comp = elf_file.base.comp;
const gpa = comp.gpa;
for (self.atoms.items) |atom_index| {
const atom = elf_file.atom(atom_index) orelse continue;
if (!atom.flags.alive) continue;
const shdr = atom.inputShdr(elf_file);
for (self.atoms_indexes.items) |atom_index| {
const atom_ptr = self.atom(atom_index) orelse continue;
if (!atom_ptr.alive) continue;
const shdr = atom_ptr.inputShdr(elf_file);
if (shdr.sh_flags & elf.SHF_ALLOC == 0) continue;
if (shdr.sh_type == elf.SHT_NOBITS) continue;
if (atom.scanRelocsRequiresCode(elf_file)) {
if (atom_ptr.scanRelocsRequiresCode(elf_file)) {
// TODO ideally, we don't have to decompress at this stage (should already be done)
// and we just fetch the code slice.
const code = try self.codeDecompressAlloc(elf_file, atom_index);
defer gpa.free(code);
try atom.scanRelocs(elf_file, code, undefs);
} else try atom.scanRelocs(elf_file, null, undefs);
try atom_ptr.scanRelocs(elf_file, code, undefs);
} else try atom_ptr.scanRelocs(elf_file, null, undefs);
}
for (self.cies.items) |cie| {
@@ -538,19 +566,21 @@ pub fn resolveSymbols(self: *Object, elf_file: *Elf) void {
if (esym.st_shndx == elf.SHN_UNDEF) continue;
if (esym.st_shndx != elf.SHN_ABS and esym.st_shndx != elf.SHN_COMMON) {
const atom_index = self.atoms.items[esym.st_shndx];
const atom = elf_file.atom(atom_index) orelse continue;
if (!atom.flags.alive) continue;
const atom_index = self.atoms_indexes.items[esym.st_shndx];
const atom_ptr = self.atom(atom_index) orelse continue;
if (!atom_ptr.alive) continue;
}
const global = elf_file.symbol(index);
if (self.asFile().symbolRank(esym, !self.alive) < global.symbolRank(elf_file)) {
const atom_index = switch (esym.st_shndx) {
elf.SHN_ABS, elf.SHN_COMMON => 0,
else => self.atoms.items[esym.st_shndx],
};
switch (esym.st_shndx) {
elf.SHN_ABS, elf.SHN_COMMON => {},
else => global.ref = .{
.index = self.atoms_indexes.items[esym.st_shndx],
.file = self.index,
},
}
global.value = @intCast(esym.st_value);
global.atom_index = atom_index;
global.esym_index = esym_index;
global.file_index = self.index;
global.version_index = elf_file.default_sym_version;
@@ -579,7 +609,7 @@ pub fn claimUnresolved(self: *Object, elf_file: *Elf) void {
};
global.value = 0;
global.atom_index = 0;
global.ref = .{ .index = 0, .file = 0 };
global.esym_index = esym_index;
global.file_index = self.index;
global.version_index = if (is_import) elf.VER_NDX_LOCAL else elf_file.default_sym_version;
@@ -600,7 +630,7 @@ pub fn claimUnresolvedObject(self: *Object, elf_file: *Elf) void {
}
global.value = 0;
global.atom_index = 0;
global.ref = .{ .index = 0, .file = 0 };
global.esym_index = esym_index;
global.file_index = self.index;
}
@@ -624,13 +654,13 @@ pub fn markLive(self: *Object, elf_file: *Elf) void {
}
}
pub fn markEhFrameAtomsDead(self: Object, elf_file: *Elf) void {
pub fn markEhFrameAtomsDead(self: *Object, elf_file: *Elf) void {
const cpu_arch = elf_file.getTarget().cpu.arch;
for (self.atoms.items) |atom_index| {
const atom = elf_file.atom(atom_index) orelse continue;
const is_eh_frame = (cpu_arch == .x86_64 and atom.inputShdr(elf_file).sh_type == elf.SHT_X86_64_UNWIND) or
mem.eql(u8, atom.name(elf_file), ".eh_frame");
if (atom.flags.alive and is_eh_frame) atom.flags.alive = false;
for (self.atoms_indexes.items) |atom_index| {
const atom_ptr = self.atom(atom_index) orelse continue;
const is_eh_frame = (cpu_arch == .x86_64 and atom_ptr.inputShdr(elf_file).sh_type == elf.SHT_X86_64_UNWIND) or
mem.eql(u8, atom_ptr.name(elf_file), ".eh_frame");
if (atom_ptr.alive and is_eh_frame) atom_ptr.alive = false;
}
}
@@ -648,9 +678,9 @@ pub fn checkDuplicates(self: *Object, dupes: anytype, elf_file: *Elf) error{OutO
sym.st_shndx == elf.SHN_COMMON) continue;
if (sym.st_shndx != elf.SHN_ABS) {
const atom_index = self.atoms.items[sym.st_shndx];
const atom = elf_file.atom(atom_index) orelse continue;
if (!atom.flags.alive) continue;
const atom_index = self.atoms_indexes.items[sym.st_shndx];
const atom_ptr = self.atom(atom_index) orelse continue;
if (!atom_ptr.alive) continue;
}
const gop = try dupes.getOrPut(index);
@@ -661,25 +691,24 @@ pub fn checkDuplicates(self: *Object, dupes: anytype, elf_file: *Elf) error{OutO
}
}
pub fn initMergeSections(self: *Object, elf_file: *Elf) !void {
pub fn initInputMergeSections(self: *Object, elf_file: *Elf) !void {
const gpa = elf_file.base.comp.gpa;
try self.merge_sections.resize(gpa, self.shdrs.items.len);
@memset(self.merge_sections.items, 0);
try self.input_merge_sections.ensureUnusedCapacity(gpa, self.shdrs.items.len);
try self.input_merge_sections_indexes.resize(gpa, self.shdrs.items.len);
@memset(self.input_merge_sections_indexes.items, 0);
for (self.shdrs.items, 0..) |shdr, shndx| {
if (shdr.sh_flags & elf.SHF_MERGE == 0) continue;
const atom_index = self.atoms.items[shndx];
const atom_ptr = elf_file.atom(atom_index) orelse continue;
if (!atom_ptr.flags.alive) continue;
const atom_index = self.atoms_indexes.items[shndx];
const atom_ptr = self.atom(atom_index) orelse continue;
if (!atom_ptr.alive) continue;
if (atom_ptr.relocs(elf_file).len > 0) continue;
const imsec_idx = try elf_file.addInputMergeSection();
const imsec = elf_file.inputMergeSection(imsec_idx).?;
self.merge_sections.items[shndx] = imsec_idx;
imsec.merge_section_index = try elf_file.getOrCreateMergeSection(atom_ptr.name(elf_file), shdr.sh_flags, shdr.sh_type);
const imsec_idx = try self.addInputMergeSection(gpa);
const imsec = self.inputMergeSection(imsec_idx).?;
self.input_merge_sections_indexes.items[shndx] = imsec_idx;
imsec.atom_index = atom_index;
const data = try self.codeDecompressAlloc(elf_file, atom_index);
@@ -734,18 +763,31 @@ pub fn initMergeSections(self: *Object, elf_file: *Elf) !void {
}
}
atom_ptr.flags.alive = false;
atom_ptr.alive = false;
}
}
pub fn initOutputMergeSections(self: *Object, elf_file: *Elf) !void {
for (self.input_merge_sections_indexes.items) |index| {
const imsec = self.inputMergeSection(index) orelse continue;
const atom_ptr = self.atom(imsec.atom_index).?;
const shdr = atom_ptr.inputShdr(elf_file);
imsec.merge_section_index = try elf_file.getOrCreateMergeSection(
atom_ptr.name(elf_file),
shdr.sh_flags,
shdr.sh_type,
);
}
}
pub fn resolveMergeSubsections(self: *Object, elf_file: *Elf) !void {
const gpa = elf_file.base.comp.gpa;
for (self.merge_sections.items) |index| {
const imsec = elf_file.inputMergeSection(index) orelse continue;
for (self.input_merge_sections_indexes.items) |index| {
const imsec = self.inputMergeSection(index) orelse continue;
if (imsec.offsets.items.len == 0) continue;
const msec = elf_file.mergeSection(imsec.merge_section_index);
const atom_ptr = elf_file.atom(imsec.atom_index).?;
const atom_ptr = self.atom(imsec.atom_index).?;
const isec = atom_ptr.inputShdr(elf_file);
try imsec.subsections.resize(gpa, imsec.strings.items.len);
@@ -754,8 +796,8 @@ pub fn resolveMergeSubsections(self: *Object, elf_file: *Elf) !void {
const string = imsec.bytes.items[str.pos..][0..str.len];
const res = try msec.insert(gpa, string);
if (!res.found_existing) {
const msub_index = try elf_file.addMergeSubsection();
const msub = elf_file.mergeSubsection(msub_index);
const msub_index = try msec.addMergeSubsection(gpa);
const msub = msec.mergeSubsection(msub_index);
msub.merge_section_index = imsec.merge_section_index;
msub.string_index = res.key.pos;
msub.alignment = atom_ptr.alignment;
@@ -776,10 +818,10 @@ pub fn resolveMergeSubsections(self: *Object, elf_file: *Elf) !void {
if (esym.st_shndx == elf.SHN_COMMON or esym.st_shndx == elf.SHN_UNDEF or esym.st_shndx == elf.SHN_ABS) continue;
const imsec_index = self.merge_sections.items[esym.st_shndx];
const imsec = elf_file.inputMergeSection(imsec_index) orelse continue;
const imsec_index = self.input_merge_sections_indexes.items[esym.st_shndx];
const imsec = self.inputMergeSection(imsec_index) orelse continue;
if (imsec.offsets.items.len == 0) continue;
const msub_index, const offset = imsec.findSubsection(@intCast(esym.st_value)) orelse {
const res = imsec.findSubsection(@intCast(esym.st_value)) orelse {
var err = try elf_file.base.addErrorWithNotes(2);
try err.addMsg("invalid symbol value: {x}", .{esym.st_value});
try err.addNote("for symbol {s}", .{sym.name(elf_file)});
@@ -787,45 +829,44 @@ pub fn resolveMergeSubsections(self: *Object, elf_file: *Elf) !void {
return error.MalformedObject;
};
try sym.addExtra(.{ .subsection = msub_index }, elf_file);
sym.ref = .{ .index = res.msub_index, .file = imsec.merge_section_index };
sym.flags.merge_subsection = true;
sym.value = offset;
sym.value = res.offset;
}
for (self.atoms.items) |atom_index| {
const atom_ptr = elf_file.atom(atom_index) orelse continue;
if (!atom_ptr.flags.alive) continue;
const extras = atom_ptr.extra(elf_file) orelse continue;
for (self.atoms_indexes.items) |atom_index| {
const atom_ptr = self.atom(atom_index) orelse continue;
if (!atom_ptr.alive) continue;
const extras = atom_ptr.extra(elf_file);
const relocs = self.relocs.items[extras.rel_index..][0..extras.rel_count];
for (relocs) |*rel| {
const esym = self.symtab.items[rel.r_sym()];
if (esym.st_type() != elf.STT_SECTION) continue;
const imsec_index = self.merge_sections.items[esym.st_shndx];
const imsec = elf_file.inputMergeSection(imsec_index) orelse continue;
const imsec_index = self.input_merge_sections_indexes.items[esym.st_shndx];
const imsec = self.inputMergeSection(imsec_index) orelse continue;
if (imsec.offsets.items.len == 0) continue;
const msub_index, const offset = imsec.findSubsection(@intCast(@as(i64, @intCast(esym.st_value)) + rel.r_addend)) orelse {
const msec = elf_file.mergeSection(imsec.merge_section_index);
const res = imsec.findSubsection(@intCast(@as(i64, @intCast(esym.st_value)) + rel.r_addend)) orelse {
var err = try elf_file.base.addErrorWithNotes(1);
try err.addMsg("invalid relocation at offset 0x{x}", .{rel.r_offset});
try err.addNote("in {}:{s}", .{ self.fmtPath(), atom_ptr.name(elf_file) });
return error.MalformedObject;
};
const msub = elf_file.mergeSubsection(msub_index);
const msec = msub.mergeSection(elf_file);
const out_sym_idx: u64 = @intCast(self.symbols.items.len);
try self.symbols.ensureUnusedCapacity(gpa, 1);
const name = try std.fmt.allocPrint(gpa, "{s}$subsection{d}", .{ msec.name(elf_file), msub_index });
const name = try std.fmt.allocPrint(gpa, "{s}$subsection{d}", .{ msec.name(elf_file), res.msub_index });
defer gpa.free(name);
const sym_index = try elf_file.addSymbol();
const sym = elf_file.symbol(sym_index);
sym.* = .{
.value = @bitCast(@as(i64, @intCast(offset)) - rel.r_addend),
.value = @bitCast(@as(i64, @intCast(res.offset)) - rel.r_addend),
.name_offset = try self.addString(gpa, name),
.esym_index = rel.r_sym(),
.file_index = self.index,
};
try sym.addExtra(.{ .subsection = msub_index }, elf_file);
sym.ref = .{ .index = res.msub_index, .file = imsec.merge_section_index };
sym.flags.merge_subsection = true;
self.symbols.addOneAssumeCapacity().* = sym_index;
rel.r_info = (out_sym_idx << 32) | rel.r_type();
@@ -857,21 +898,10 @@ pub fn convertCommonSymbols(self: *Object, elf_file: *Elf) !void {
const comp = elf_file.base.comp;
const gpa = comp.gpa;
const atom_index = try elf_file.addAtom();
try self.atoms.append(gpa, atom_index);
const is_tls = global.type(elf_file) == elf.STT_TLS;
const name = if (is_tls) ".tls_common" else ".common";
const atom = elf_file.atom(atom_index).?;
const name_offset = @as(u32, @intCast(self.strtab.items.len));
try self.strtab.writer(gpa).print("{s}\x00", .{name});
atom.atom_index = atom_index;
atom.name_offset = name_offset;
atom.file_index = self.index;
atom.size = this_sym.st_size;
const alignment = this_sym.st_value;
atom.alignment = Alignment.fromNonzeroByteUnits(alignment);
var sh_flags: u32 = elf.SHF_ALLOC | elf.SHF_WRITE;
if (is_tls) sh_flags |= elf.SHF_TLS;
@@ -887,78 +917,84 @@ pub fn convertCommonSymbols(self: *Object, elf_file: *Elf) !void {
.sh_size = sh_size,
.sh_link = 0,
.sh_info = 0,
.sh_addralign = alignment,
.sh_addralign = this_sym.st_value,
.sh_entsize = 0,
};
atom.input_section_index = shndx;
const atom_index = try self.addAtom(gpa, .{
.name = name_offset,
.shndx = shndx,
.size = this_sym.st_size,
.alignment = Alignment.fromNonzeroByteUnits(this_sym.st_value),
});
try self.atoms_indexes.append(gpa, atom_index);
global.value = 0;
global.atom_index = atom_index;
global.ref = .{ .index = atom_index, .file = self.index };
global.flags.weak = false;
}
}
pub fn initOutputSections(self: Object, elf_file: *Elf) !void {
for (self.atoms.items) |atom_index| {
const atom = elf_file.atom(atom_index) orelse continue;
if (!atom.flags.alive) continue;
const shdr = atom.inputShdr(elf_file);
pub fn resolveComdatGroups(self: *Object, elf_file: *Elf, table: anytype) !void {
for (self.comdat_groups.items, 0..) |*cg, cgi| {
const signature = cg.signature(elf_file);
const gop = try table.getOrPut(signature);
if (!gop.found_existing) {
gop.value_ptr.* = .{ .index = @intCast(cgi), .file = self.index };
continue;
}
const current = elf_file.comdatGroup(gop.value_ptr.*);
cg.alive = false;
if (self.index < current.file_index) {
current.alive = false;
cg.alive = true;
gop.value_ptr.* = .{ .index = @intCast(cgi), .file = self.index };
}
}
}
pub fn markComdatGroupsDead(self: *Object, elf_file: *Elf) void {
for (self.comdat_groups.items) |cg| {
if (cg.alive) continue;
for (cg.comdatGroupMembers(elf_file)) |shndx| {
const atom_index = self.atoms_indexes.items[shndx];
if (self.atom(atom_index)) |atom_ptr| {
atom_ptr.alive = false;
atom_ptr.markFdesDead(elf_file);
}
}
}
}
pub fn initOutputSections(self: *Object, elf_file: *Elf) !void {
for (self.atoms_indexes.items) |atom_index| {
const atom_ptr = self.atom(atom_index) orelse continue;
if (!atom_ptr.alive) continue;
const shdr = atom_ptr.inputShdr(elf_file);
_ = try self.initOutputSection(elf_file, shdr);
}
}
pub fn addAtomsToOutputSections(self: *Object, elf_file: *Elf) !void {
for (self.atoms.items) |atom_index| {
const atom = elf_file.atom(atom_index) orelse continue;
if (!atom.flags.alive) continue;
const shdr = atom.inputShdr(elf_file);
atom.output_section_index = self.initOutputSection(elf_file, shdr) catch unreachable;
for (self.atoms_indexes.items) |atom_index| {
const atom_ptr = self.atom(atom_index) orelse continue;
if (!atom_ptr.alive) continue;
const shdr = atom_ptr.inputShdr(elf_file);
atom_ptr.output_section_index = self.initOutputSection(elf_file, shdr) catch unreachable;
const comp = elf_file.base.comp;
const gpa = comp.gpa;
const gop = try elf_file.output_sections.getOrPut(gpa, atom.output_section_index);
const gop = try elf_file.output_sections.getOrPut(gpa, atom_ptr.output_section_index);
if (!gop.found_existing) gop.value_ptr.* = .{};
try gop.value_ptr.append(gpa, atom_index);
}
for (self.locals()) |local_index| {
const local = elf_file.symbol(local_index);
if (local.mergeSubsection(elf_file)) |msub| {
if (!msub.alive) continue;
local.output_section_index = msub.mergeSection(elf_file).output_section_index;
continue;
}
const atom = local.atom(elf_file) orelse continue;
if (!atom.flags.alive) continue;
local.output_section_index = atom.output_section_index;
}
for (self.globals()) |global_index| {
const global = elf_file.symbol(global_index);
if (global.file(elf_file).?.index() != self.index) continue;
if (global.mergeSubsection(elf_file)) |msub| {
if (!msub.alive) continue;
global.output_section_index = msub.mergeSection(elf_file).output_section_index;
continue;
}
const atom = global.atom(elf_file) orelse continue;
if (!atom.flags.alive) continue;
global.output_section_index = atom.output_section_index;
}
for (self.symbols.items[self.symtab.items.len..]) |local_index| {
const local = elf_file.symbol(local_index);
const msub = local.mergeSubsection(elf_file).?;
if (!msub.alive) continue;
local.output_section_index = msub.mergeSection(elf_file).output_section_index;
try gop.value_ptr.append(gpa, .{ .index = atom_index, .file = self.index });
}
}
pub fn initRelaSections(self: Object, elf_file: *Elf) !void {
for (self.atoms.items) |atom_index| {
const atom = elf_file.atom(atom_index) orelse continue;
if (!atom.flags.alive) continue;
const shndx = atom.relocsShndx() orelse continue;
pub fn initRelaSections(self: *Object, elf_file: *Elf) !void {
for (self.atoms_indexes.items) |atom_index| {
const atom_ptr = self.atom(atom_index) orelse continue;
if (!atom_ptr.alive) continue;
const shndx = atom_ptr.relocsShndx() orelse continue;
const shdr = self.shdrs.items[shndx];
const out_shndx = try self.initOutputSection(elf_file, shdr);
const out_shdr = &elf_file.shdrs.items[out_shndx];
@@ -968,24 +1004,24 @@ pub fn initRelaSections(self: Object, elf_file: *Elf) !void {
}
}
pub fn addAtomsToRelaSections(self: Object, elf_file: *Elf) !void {
for (self.atoms.items) |atom_index| {
const atom = elf_file.atom(atom_index) orelse continue;
if (!atom.flags.alive) continue;
pub fn addAtomsToRelaSections(self: *Object, elf_file: *Elf) !void {
for (self.atoms_indexes.items) |atom_index| {
const atom_ptr = self.atom(atom_index) orelse continue;
if (!atom_ptr.alive) continue;
const shndx = blk: {
const shndx = atom.relocsShndx() orelse continue;
const shndx = atom_ptr.relocsShndx() orelse continue;
const shdr = self.shdrs.items[shndx];
break :blk self.initOutputSection(elf_file, shdr) catch unreachable;
};
const shdr = &elf_file.shdrs.items[shndx];
shdr.sh_info = atom.outputShndx().?;
shdr.sh_info = atom_ptr.output_section_index;
shdr.sh_link = elf_file.symtab_section_index.?;
const comp = elf_file.base.comp;
const gpa = comp.gpa;
const gop = try elf_file.output_rela_sections.getOrPut(gpa, atom.outputShndx().?);
const gop = try elf_file.output_rela_sections.getOrPut(gpa, atom_ptr.output_section_index);
if (!gop.found_existing) gop.value_ptr.* = .{ .shndx = shndx };
try gop.value_ptr.atom_list.append(gpa, atom_index);
try gop.value_ptr.atom_list.append(gpa, .{ .index = atom_index, .file = self.index });
}
}
@@ -1041,7 +1077,7 @@ pub fn updateSymtabSize(self: *Object, elf_file: *Elf) !void {
const isAlive = struct {
fn isAlive(sym: *const Symbol, ctx: *Elf) bool {
if (sym.mergeSubsection(ctx)) |msub| return msub.alive;
if (sym.atom(ctx)) |atom_ptr| return atom_ptr.flags.alive;
if (sym.atom(ctx)) |atom_ptr| return atom_ptr.alive;
return true;
}
}.isAlive;
@@ -1119,11 +1155,10 @@ pub fn globals(self: Object) []const Symbol.Index {
/// Returns atom's code and optionally uncompresses data if required (for compressed sections).
/// Caller owns the memory.
pub fn codeDecompressAlloc(self: Object, elf_file: *Elf, atom_index: Atom.Index) ![]u8 {
pub fn codeDecompressAlloc(self: *Object, elf_file: *Elf, atom_index: Atom.Index) ![]u8 {
const comp = elf_file.base.comp;
const gpa = comp.gpa;
const atom_ptr = elf_file.atom(atom_index).?;
assert(atom_ptr.file_index == self.index);
const atom_ptr = self.atom(atom_index).?;
const shdr = atom_ptr.inputShdr(elf_file);
const handle = elf_file.fileHandle(self.file_handle);
const data = try self.preadShdrContentsAlloc(gpa, handle, atom_ptr.input_section_index);
@@ -1184,6 +1219,105 @@ fn preadRelocsAlloc(self: Object, allocator: Allocator, handle: std.fs.File, shn
return @as([*]align(1) const elf.Elf64_Rela, @ptrCast(raw.ptr))[0..num];
}
const AddAtomArgs = struct {
name: u32,
shndx: u32,
size: u64,
alignment: Alignment,
};
fn addAtom(self: *Object, allocator: Allocator, args: AddAtomArgs) !Atom.Index {
try self.atoms.ensureUnusedCapacity(allocator, 1);
try self.atoms_extra.ensureUnusedCapacity(allocator, @sizeOf(Atom.Extra));
return self.addAtomAssumeCapacity(args);
}
fn addAtomAssumeCapacity(self: *Object, args: AddAtomArgs) Atom.Index {
const atom_index: Atom.Index = @intCast(self.atoms.items.len);
const atom_ptr = self.atoms.addOneAssumeCapacity();
atom_ptr.* = .{
.atom_index = atom_index,
.name_offset = args.name,
.file_index = self.index,
.input_section_index = args.shndx,
.extra_index = self.addAtomExtraAssumeCapacity(.{}),
.size = args.size,
.alignment = args.alignment,
};
return atom_index;
}
pub fn atom(self: *Object, atom_index: Atom.Index) ?*Atom {
if (atom_index == 0) return null;
assert(atom_index < self.atoms.items.len);
return &self.atoms.items[atom_index];
}
pub fn addAtomExtra(self: *Object, allocator: Allocator, extra: Atom.Extra) !u32 {
const fields = @typeInfo(Atom.Extra).Struct.fields;
try self.atoms_extra.ensureUnusedCapacity(allocator, fields.len);
return self.addAtomExtraAssumeCapacity(extra);
}
pub fn addAtomExtraAssumeCapacity(self: *Object, extra: Atom.Extra) u32 {
const index = @as(u32, @intCast(self.atoms_extra.items.len));
const fields = @typeInfo(Atom.Extra).Struct.fields;
inline for (fields) |field| {
self.atoms_extra.appendAssumeCapacity(switch (field.type) {
u32 => @field(extra, field.name),
else => @compileError("bad field type"),
});
}
return index;
}
pub fn atomExtra(self: *Object, index: u32) Atom.Extra {
const fields = @typeInfo(Atom.Extra).Struct.fields;
var i: usize = index;
var result: Atom.Extra = undefined;
inline for (fields) |field| {
@field(result, field.name) = switch (field.type) {
u32 => self.atoms_extra.items[i],
else => @compileError("bad field type"),
};
i += 1;
}
return result;
}
pub fn setAtomExtra(self: *Object, index: u32, extra: Atom.Extra) void {
const fields = @typeInfo(Atom.Extra).Struct.fields;
inline for (fields, 0..) |field, i| {
self.atoms_extra.items[index + i] = switch (field.type) {
u32 => @field(extra, field.name),
else => @compileError("bad field type"),
};
}
}
fn addInputMergeSection(self: *Object, allocator: Allocator) !InputMergeSection.Index {
const index: InputMergeSection.Index = @intCast(self.input_merge_sections.items.len);
const msec = try self.input_merge_sections.addOne(allocator);
msec.* = .{};
return index;
}
fn inputMergeSection(self: *Object, index: InputMergeSection.Index) ?*InputMergeSection {
if (index == 0) return null;
return &self.input_merge_sections.items[index];
}
fn addComdatGroup(self: *Object, allocator: Allocator) !Elf.ComdatGroup.Index {
const index = @as(Elf.ComdatGroup.Index, @intCast(self.comdat_groups.items.len));
_ = try self.comdat_groups.addOne(allocator);
return index;
}
pub fn comdatGroup(self: *Object, index: Elf.ComdatGroup.Index) *Elf.ComdatGroup {
assert(index < self.comdat_groups.items.len);
return &self.comdat_groups.items[index];
}
pub fn format(
self: *Object,
comptime unused_fmt_string: []const u8,
@@ -1247,9 +1381,9 @@ fn formatAtoms(
_ = options;
const object = ctx.object;
try writer.writeAll(" atoms\n");
for (object.atoms.items) |atom_index| {
const atom = ctx.elf_file.atom(atom_index) orelse continue;
try writer.print(" {}\n", .{atom.fmt(ctx.elf_file)});
for (object.atoms_indexes.items) |atom_index| {
const atom_ptr = object.atom(atom_index) orelse continue;
try writer.print(" {}\n", .{atom_ptr.fmt(ctx.elf_file)});
}
}
@@ -1315,16 +1449,15 @@ fn formatComdatGroups(
const object = ctx.object;
const elf_file = ctx.elf_file;
try writer.writeAll(" COMDAT groups\n");
for (object.comdat_groups.items) |cg_index| {
const cg = elf_file.comdatGroup(cg_index);
const cg_owner = elf_file.comdatGroupOwner(cg.owner);
if (cg_owner.file != object.index) continue;
try writer.print(" COMDAT({d})\n", .{cg_index});
for (object.comdat_groups.items, 0..) |cg, cg_index| {
try writer.print(" COMDAT({d})", .{cg_index});
if (!cg.alive) try writer.writeAll(" : [*]");
try writer.writeByte('\n');
const cg_members = cg.comdatGroupMembers(elf_file);
for (cg_members) |shndx| {
const atom_index = object.atoms.items[shndx];
const atom = elf_file.atom(atom_index) orelse continue;
try writer.print(" atom({d}) : {s}\n", .{ atom_index, atom.name(elf_file) });
const atom_index = object.atoms_indexes.items[shndx];
const atom_ptr = object.atom(atom_index) orelse continue;
try writer.print(" atom({d}) : {s}\n", .{ atom_index, atom_ptr.name(elf_file) });
}
}
}

View File

@@ -232,7 +232,7 @@ pub fn resolveSymbols(self: *SharedObject, elf_file: *Elf) void {
const global = elf_file.symbol(index);
if (self.asFile().symbolRank(this_sym, false) < global.symbolRank(elf_file)) {
global.value = @intCast(this_sym.st_value);
global.atom_index = 0;
global.ref = .{ .index = 0, .file = 0 };
global.esym_index = esym_index;
global.version_index = self.versyms.items[esym_index];
global.file_index = self.index;

View File

@@ -9,10 +9,9 @@ name_offset: u32 = 0,
/// Index of file where this symbol is defined.
file_index: File.Index = 0,
/// Index of atom containing this symbol.
/// Index of 0 means there is no associated atom with this symbol.
/// Use `atom` to get the pointer to the atom.
atom_index: Atom.Index = 0,
/// Reference to Atom or merge subsection containing this symbol if any.
/// Use `atom` or `mergeSubsection` to get the pointer to the atom.
ref: Elf.Ref = .{ .index = 0, .file = 0 },
/// Assigned output section index for this symbol.
output_section_index: u32 = 0,
@@ -34,11 +33,15 @@ pub fn isAbs(symbol: Symbol, elf_file: *Elf) bool {
const file_ptr = symbol.file(elf_file).?;
if (file_ptr == .shared_object) return symbol.elfSym(elf_file).st_shndx == elf.SHN_ABS;
return !symbol.flags.import and symbol.atom(elf_file) == null and
symbol.mergeSubsection(elf_file) == null and symbol.outputShndx() == null and
symbol.mergeSubsection(elf_file) == null and symbol.outputShndx(elf_file) == null and
file_ptr != .linker_defined;
}
pub fn outputShndx(symbol: Symbol) ?u32 {
pub fn outputShndx(symbol: Symbol, elf_file: *Elf) ?u32 {
if (symbol.mergeSubsection(elf_file)) |msub|
return if (msub.alive) msub.mergeSection(elf_file).output_section_index else null;
if (symbol.atom(elf_file)) |atom_ptr|
return if (atom_ptr.alive) atom_ptr.output_section_index else null;
if (symbol.output_section_index == 0) return null;
return symbol.output_section_index;
}
@@ -68,13 +71,15 @@ pub fn name(symbol: Symbol, elf_file: *Elf) [:0]const u8 {
}
pub fn atom(symbol: Symbol, elf_file: *Elf) ?*Atom {
return elf_file.atom(symbol.atom_index);
if (symbol.flags.merge_subsection) return null;
const file_ptr = elf_file.file(symbol.ref.file) orelse return null;
return file_ptr.atom(symbol.ref.index);
}
pub fn mergeSubsection(symbol: Symbol, elf_file: *Elf) ?*MergeSubsection {
if (!symbol.flags.merge_subsection) return null;
const extras = symbol.extra(elf_file).?;
return elf_file.mergeSubsection(extras.subsection);
const msec = elf_file.mergeSection(symbol.ref.file);
return msec.mergeSubsection(symbol.ref.index);
}
pub fn file(symbol: Symbol, elf_file: *Elf) ?File {
@@ -116,7 +121,7 @@ pub fn address(symbol: Symbol, opts: struct { plt: bool = true }, elf_file: *Elf
return symbol.pltAddress(elf_file);
}
if (symbol.atom(elf_file)) |atom_ptr| {
if (!atom_ptr.flags.alive) {
if (!atom_ptr.alive) {
if (mem.eql(u8, atom_ptr.name(elf_file), ".eh_frame")) {
const sym_name = symbol.name(elf_file);
const sh_addr, const sh_size = blk: {
@@ -258,7 +263,6 @@ const AddExtraOpts = struct {
gottp: ?u32 = null,
tlsdesc: ?u32 = null,
zig_got: ?u32 = null,
subsection: ?u32 = null,
};
pub fn addExtra(symbol: *Symbol, opts: AddExtraOpts, elf_file: *Elf) !void {
@@ -298,7 +302,7 @@ pub fn setOutputSym(symbol: Symbol, elf_file: *Elf, out: *elf.Elf64_Sym) void {
if (elf_file.base.isRelocatable() and esym.st_shndx == elf.SHN_COMMON) break :blk elf.SHN_COMMON;
if (symbol.mergeSubsection(elf_file)) |msub| break :blk @intCast(msub.mergeSection(elf_file).output_section_index);
if (symbol.atom(elf_file) == null and file_ptr != .linker_defined) break :blk elf.SHN_ABS;
break :blk @intCast(symbol.outputShndx() orelse elf.SHN_UNDEF);
break :blk @intCast(symbol.outputShndx(elf_file) orelse elf.SHN_UNDEF);
};
const st_value = blk: {
if (symbol.flags.has_copy_rel) break :blk symbol.address(.{}, elf_file);
@@ -382,22 +386,23 @@ fn format2(
_ = options;
_ = unused_fmt_string;
const symbol = ctx.symbol;
const elf_file = ctx.elf_file;
try writer.print("%{d} : {s} : @{x}", .{
symbol.esym_index,
symbol.fmtName(ctx.elf_file),
symbol.address(.{}, ctx.elf_file),
symbol.fmtName(elf_file),
symbol.address(.{}, elf_file),
});
if (symbol.file(ctx.elf_file)) |file_ptr| {
if (symbol.isAbs(ctx.elf_file)) {
if (symbol.elfSym(ctx.elf_file).st_shndx == elf.SHN_UNDEF) {
if (symbol.file(elf_file)) |file_ptr| {
if (symbol.isAbs(elf_file)) {
if (symbol.elfSym(elf_file).st_shndx == elf.SHN_UNDEF) {
try writer.writeAll(" : undef");
} else {
try writer.writeAll(" : absolute");
}
} else if (symbol.outputShndx()) |shndx| {
} else if (symbol.outputShndx(elf_file)) |shndx| {
try writer.print(" : shdr({d})", .{shndx});
}
if (symbol.atom(ctx.elf_file)) |atom_ptr| {
if (symbol.atom(elf_file)) |atom_ptr| {
try writer.print(" : atom({d})", .{atom_ptr.atom_index});
}
var buf: [2]u8 = .{'_'} ** 2;
@@ -483,7 +488,7 @@ pub const Extra = struct {
gottp: u32 = 0,
tlsdesc: u32 = 0,
zig_got: u32 = 0,
subsection: u32 = 0,
merge_section: u32 = 0,
};
pub const Index = u32;

View File

@@ -15,7 +15,9 @@ local_symbols: std.ArrayListUnmanaged(Symbol.Index) = .{},
global_symbols: std.ArrayListUnmanaged(Symbol.Index) = .{},
globals_lookup: std.AutoHashMapUnmanaged(u32, Symbol.Index) = .{},
atoms: std.ArrayListUnmanaged(Atom.Index) = .{},
atoms: std.ArrayListUnmanaged(Atom) = .{},
atoms_indexes: std.ArrayListUnmanaged(Atom.Index) = .{},
atoms_extra: std.ArrayListUnmanaged(u32) = .{},
relocs: std.ArrayListUnmanaged(std.ArrayListUnmanaged(elf.Elf64_Rela)) = .{},
num_dynrelocs: u32 = 0,
@@ -80,7 +82,7 @@ pub fn init(self: *ZigObject, elf_file: *Elf) !void {
const comp = elf_file.base.comp;
const gpa = comp.gpa;
try self.atoms.append(gpa, 0); // null input section
try self.atoms.append(gpa, .{ .extra_index = try self.addAtomExtra(gpa, .{}) }); // null input section
try self.relocs.append(gpa, .{}); // null relocs section
try self.strtab.buffer.append(gpa, 0);
@@ -117,6 +119,8 @@ pub fn deinit(self: *ZigObject, allocator: Allocator) void {
self.global_symbols.deinit(allocator);
self.globals_lookup.deinit(allocator);
self.atoms.deinit(allocator);
self.atoms_indexes.deinit(allocator);
self.atoms_extra.deinit(allocator);
for (self.relocs.items) |*list| {
list.deinit(allocator);
}
@@ -276,24 +280,20 @@ pub fn addGlobalEsym(self: *ZigObject, allocator: Allocator) !Symbol.Index {
return index | global_symbol_bit;
}
pub fn addAtom(self: *ZigObject, elf_file: *Elf) !Symbol.Index {
pub fn newAtom(self: *ZigObject, elf_file: *Elf) !Symbol.Index {
const gpa = elf_file.base.comp.gpa;
const atom_index = try elf_file.addAtom();
const atom_index = try self.addAtom(gpa);
const symbol_index = try elf_file.addSymbol();
const esym_index = try self.addLocalEsym(gpa);
const shndx = @as(u32, @intCast(self.atoms.items.len));
try self.atoms.append(gpa, atom_index);
try self.atoms_indexes.append(gpa, atom_index);
try self.local_symbols.append(gpa, symbol_index);
const atom_ptr = elf_file.atom(atom_index).?;
atom_ptr.file_index = self.index;
const symbol_ptr = elf_file.symbol(symbol_index);
symbol_ptr.file_index = self.index;
symbol_ptr.atom_index = atom_index;
symbol_ptr.ref = .{ .index = atom_index, .file = self.index };
self.local_esyms.items(.shndx)[esym_index] = shndx;
self.local_esyms.items(.shndx)[esym_index] = atom_index;
self.local_esyms.items(.elf_sym)[esym_index].st_shndx = SHN_ATOM;
symbol_ptr.esym_index = esym_index;
@@ -301,21 +301,22 @@ pub fn addAtom(self: *ZigObject, elf_file: *Elf) !Symbol.Index {
const relocs_index = @as(u32, @intCast(self.relocs.items.len));
const relocs = try self.relocs.addOne(gpa);
relocs.* = .{};
const atom_ptr = self.atom(atom_index).?;
atom_ptr.relocs_section_index = relocs_index;
return symbol_index;
}
/// TODO actually create fake input shdrs and return that instead.
pub fn inputShdr(self: ZigObject, atom_index: Atom.Index, elf_file: *Elf) elf.Elf64_Shdr {
_ = self;
const atom = elf_file.atom(atom_index) orelse return Elf.null_shdr;
const shndx = atom.outputShndx() orelse return Elf.null_shdr;
pub fn inputShdr(self: *ZigObject, atom_index: Atom.Index, elf_file: *Elf) elf.Elf64_Shdr {
const atom_ptr = self.atom(atom_index) orelse return Elf.null_shdr;
const shndx = atom_ptr.output_section_index;
var shdr = elf_file.shdrs.items[shndx];
shdr.sh_addr = 0;
shdr.sh_offset = 0;
shdr.sh_size = atom.size;
shdr.sh_addralign = atom.alignment.toByteUnits() orelse 1;
shdr.sh_size = atom_ptr.size;
shdr.sh_addralign = atom_ptr.alignment.toByteUnits() orelse 1;
return shdr;
}
@@ -329,27 +330,21 @@ pub fn resolveSymbols(self: *ZigObject, elf_file: *Elf) void {
if (esym.st_shndx != elf.SHN_ABS and esym.st_shndx != elf.SHN_COMMON) {
assert(esym.st_shndx == SHN_ATOM);
const atom_index = self.atoms.items[shndx];
const atom = elf_file.atom(atom_index) orelse continue;
if (!atom.flags.alive) continue;
const atom_ptr = self.atom(shndx) orelse continue;
if (!atom_ptr.alive) continue;
}
const global = elf_file.symbol(index);
if (self.asFile().symbolRank(esym, false) < global.symbolRank(elf_file)) {
const atom_index = switch (esym.st_shndx) {
elf.SHN_ABS, elf.SHN_COMMON => 0,
SHN_ATOM => self.atoms.items[shndx],
SHN_ATOM => shndx,
else => unreachable,
};
const output_section_index = if (elf_file.atom(atom_index)) |atom|
atom.outputShndx().?
else
elf.SHN_UNDEF;
global.value = @intCast(esym.st_value);
global.atom_index = atom_index;
global.ref = .{ .index = atom_index, .file = self.index };
global.esym_index = esym_index;
global.file_index = self.index;
global.output_section_index = output_section_index;
global.version_index = elf_file.default_sym_version;
if (esym.st_bind() == elf.STB_WEAK) global.flags.weak = true;
}
@@ -376,7 +371,7 @@ pub fn claimUnresolved(self: ZigObject, elf_file: *Elf) void {
};
global.value = 0;
global.atom_index = 0;
global.ref = .{ .index = 0, .file = 0 };
global.esym_index = esym_index;
global.file_index = self.index;
global.version_index = if (is_import) elf.VER_NDX_LOCAL else elf_file.default_sym_version;
@@ -397,7 +392,7 @@ pub fn claimUnresolvedObject(self: ZigObject, elf_file: *Elf) void {
}
global.value = 0;
global.atom_index = 0;
global.ref = .{ .index = 0, .file = 0 };
global.esym_index = esym_index;
global.file_index = self.index;
}
@@ -405,19 +400,19 @@ pub fn claimUnresolvedObject(self: ZigObject, elf_file: *Elf) void {
pub fn scanRelocs(self: *ZigObject, elf_file: *Elf, undefs: anytype) !void {
const gpa = elf_file.base.comp.gpa;
for (self.atoms.items) |atom_index| {
const atom = elf_file.atom(atom_index) orelse continue;
if (!atom.flags.alive) continue;
const shdr = atom.inputShdr(elf_file);
for (self.atoms_indexes.items) |atom_index| {
const atom_ptr = self.atom(atom_index) orelse continue;
if (!atom_ptr.alive) continue;
const shdr = atom_ptr.inputShdr(elf_file);
if (shdr.sh_type == elf.SHT_NOBITS) continue;
if (atom.scanRelocsRequiresCode(elf_file)) {
if (atom_ptr.scanRelocsRequiresCode(elf_file)) {
// TODO ideally we don't have to fetch the code here.
// Perhaps it would make sense to save the code until flushModule where we
// would free all of generated code?
const code = try self.codeAlloc(elf_file, atom_index);
defer gpa.free(code);
try atom.scanRelocs(elf_file, code, undefs);
} else try atom.scanRelocs(elf_file, null, undefs);
try atom_ptr.scanRelocs(elf_file, code, undefs);
} else try atom_ptr.scanRelocs(elf_file, null, undefs);
}
}
@@ -450,9 +445,8 @@ pub fn checkDuplicates(self: *ZigObject, dupes: anytype, elf_file: *Elf) error{O
esym.st_shndx == elf.SHN_COMMON) continue;
if (esym.st_shndx == SHN_ATOM) {
const atom_index = self.atoms.items[shndx];
const atom = elf_file.atom(atom_index) orelse continue;
if (!atom.flags.alive) continue;
const atom_ptr = self.atom(shndx) orelse continue;
if (!atom_ptr.alive) continue;
}
const gop = try dupes.getOrPut(index);
@@ -493,7 +487,7 @@ pub fn updateArSymtab(self: ZigObject, ar_symtab: *Archive.ArSymtab, elf_file: *
const global = elf_file.symbol(global_index);
const file_ptr = global.file(elf_file).?;
assert(file_ptr.index() == self.index);
if (global.outputShndx() == null) continue;
if (global.outputShndx(elf_file) == null) continue;
const off = try ar_symtab.strtab.insert(gpa, global.name(elf_file));
ar_symtab.symtab.appendAssumeCapacity(.{ .off = off, .file_index = self.index });
@@ -517,20 +511,20 @@ pub fn writeAr(self: ZigObject, writer: anytype) !void {
try writer.writeAll(self.data.items);
}
pub fn addAtomsToRelaSections(self: ZigObject, elf_file: *Elf) !void {
for (self.atoms.items) |atom_index| {
const atom = elf_file.atom(atom_index) orelse continue;
if (!atom.flags.alive) continue;
const rela_shndx = atom.relocsShndx() orelse continue;
pub fn addAtomsToRelaSections(self: *ZigObject, elf_file: *Elf) !void {
for (self.atoms_indexes.items) |atom_index| {
const atom_ptr = self.atom(atom_index) orelse continue;
if (!atom_ptr.alive) continue;
const rela_shndx = atom_ptr.relocsShndx() orelse continue;
// TODO this check will become obsolete when we rework our relocs mechanism at the ZigObject level
if (self.relocs.items[rela_shndx].items.len == 0) continue;
const out_shndx = atom.outputShndx().?;
const out_shndx = atom_ptr.output_section_index;
const out_shdr = elf_file.shdrs.items[out_shndx];
if (out_shdr.sh_type == elf.SHT_NOBITS) continue;
const gpa = elf_file.base.comp.gpa;
const sec = elf_file.output_rela_sections.getPtr(out_shndx).?;
try sec.atom_list.append(gpa, atom_index);
try sec.atom_list.append(gpa, .{ .index = atom_index, .file = self.index });
}
}
@@ -561,7 +555,7 @@ pub fn globals(self: ZigObject) []const Symbol.Index {
pub fn updateSymtabSize(self: *ZigObject, elf_file: *Elf) !void {
for (self.locals()) |local_index| {
const local = elf_file.symbol(local_index);
if (local.atom(elf_file)) |atom| if (!atom.flags.alive) continue;
if (local.atom(elf_file)) |atom_ptr| if (!atom_ptr.alive) continue;
const esym = local.elfSym(elf_file);
switch (esym.st_type()) {
elf.STT_SECTION, elf.STT_NOTYPE => continue,
@@ -577,7 +571,7 @@ pub fn updateSymtabSize(self: *ZigObject, elf_file: *Elf) !void {
const global = elf_file.symbol(global_index);
const file_ptr = global.file(elf_file) orelse continue;
if (file_ptr.index() != self.index) continue;
if (global.atom(elf_file)) |atom| if (!atom.flags.alive) continue;
if (global.atom(elf_file)) |atom_ptr| if (!atom_ptr.alive) continue;
global.flags.output_symtab = true;
if (global.isLocal(elf_file)) {
try global.addExtra(.{ .symtab = self.output_symtab_ctx.nlocals }, elf_file);
@@ -621,11 +615,10 @@ pub fn asFile(self: *ZigObject) File {
/// Returns atom's code.
/// Caller owns the memory.
pub fn codeAlloc(self: ZigObject, elf_file: *Elf, atom_index: Atom.Index) ![]u8 {
pub fn codeAlloc(self: *ZigObject, elf_file: *Elf, atom_index: Atom.Index) ![]u8 {
const gpa = elf_file.base.comp.gpa;
const atom = elf_file.atom(atom_index).?;
assert(atom.file_index == self.index);
const shdr = &elf_file.shdrs.items[atom.outputShndx().?];
const atom_ptr = self.atom(atom_index).?;
const shdr = &elf_file.shdrs.items[atom_ptr.output_section_index];
if (shdr.sh_flags & elf.SHF_TLS != 0) {
const tlv = self.tls_variables.get(atom_index).?;
@@ -633,13 +626,13 @@ pub fn codeAlloc(self: ZigObject, elf_file: *Elf, atom_index: Atom.Index) ![]u8
return code;
}
const file_offset = shdr.sh_offset + @as(u64, @intCast(atom.value));
const size = std.math.cast(usize, atom.size) orelse return error.Overflow;
const file_offset = shdr.sh_offset + @as(u64, @intCast(atom_ptr.value));
const size = std.math.cast(usize, atom_ptr.size) orelse return error.Overflow;
const code = try gpa.alloc(u8, size);
errdefer gpa.free(code);
const amt = try elf_file.base.file.?.preadAll(code, file_offset);
if (amt != code.len) {
log.err("fetching code for {s} failed", .{atom.name(elf_file)});
log.err("fetching code for {s} failed", .{atom_ptr.name(elf_file)});
return error.InputOutput;
}
return code;
@@ -760,7 +753,7 @@ pub fn getOrCreateMetadataForLazySymbol(
};
switch (metadata.state.*) {
.unused => {
const symbol_index = try self.addAtom(elf_file);
const symbol_index = try self.newAtom(elf_file);
const sym = elf_file.symbol(symbol_index);
sym.flags.needs_zig_got = true;
metadata.symbol_index.* = symbol_index;
@@ -824,7 +817,7 @@ pub fn getOrCreateMetadataForDecl(
const gop = try self.decls.getOrPut(gpa, decl_index);
if (!gop.found_existing) {
const any_non_single_threaded = elf_file.base.comp.config.any_non_single_threaded;
const symbol_index = try self.addAtom(elf_file);
const symbol_index = try self.newAtom(elf_file);
const mod = elf_file.base.comp.module.?;
const decl = mod.declPtr(decl_index);
const sym = elf_file.symbol(symbol_index);
@@ -861,14 +854,14 @@ fn getDeclShdrIndex(
if (is_all_zeroes) break :blk elf_file.sectionByName(".tbss") orelse try elf_file.addSection(.{
.type = elf.SHT_NOBITS,
.flags = elf.SHF_ALLOC | elf.SHF_WRITE | elf.SHF_TLS,
.name = ".tbss",
.name = try elf_file.insertShString(".tbss"),
.offset = std.math.maxInt(u64),
});
break :blk elf_file.sectionByName(".tdata") orelse try elf_file.addSection(.{
.type = elf.SHT_PROGBITS,
.flags = elf.SHF_ALLOC | elf.SHF_WRITE | elf.SHF_TLS,
.name = ".tdata",
.name = try elf_file.insertShString(".tdata"),
.offset = std.math.maxInt(u64),
});
}
@@ -919,14 +912,13 @@ fn updateDeclCode(
const sym = elf_file.symbol(sym_index);
const esym = &self.local_esyms.items(.elf_sym)[sym.esym_index];
const atom_ptr = sym.atom(elf_file).?;
const name_offset = try self.strtab.insert(gpa, decl.fqn.toSlice(ip));
sym.output_section_index = shdr_index;
atom_ptr.alive = true;
atom_ptr.name_offset = name_offset;
atom_ptr.output_section_index = shdr_index;
sym.name_offset = try self.strtab.insert(gpa, decl.fqn.toSlice(ip));
atom_ptr.flags.alive = true;
atom_ptr.name_offset = sym.name_offset;
esym.st_name = sym.name_offset;
sym.name_offset = name_offset;
esym.st_name = name_offset;
esym.st_info |= stt_bits;
esym.st_size = code.len;
@@ -1018,16 +1010,17 @@ fn updateTlv(
const sym = elf_file.symbol(sym_index);
const esym = &self.local_esyms.items(.elf_sym)[sym.esym_index];
const atom_ptr = sym.atom(elf_file).?;
const name_offset = try self.strtab.insert(gpa, decl.fqn.toSlice(ip));
sym.value = 0;
sym.output_section_index = shndx;
atom_ptr.output_section_index = shndx;
sym.name_offset = name_offset;
atom_ptr.output_section_index = shndx;
atom_ptr.alive = true;
atom_ptr.name_offset = name_offset;
sym.name_offset = try self.strtab.insert(gpa, decl.fqn.toSlice(ip));
atom_ptr.flags.alive = true;
atom_ptr.name_offset = sym.name_offset;
esym.st_value = 0;
esym.st_name = sym.name_offset;
esym.st_name = name_offset;
esym.st_info = elf.STT_TLS;
esym.st_size = code.len;
@@ -1048,7 +1041,7 @@ fn updateTlv(
{
const gop = try elf_file.output_sections.getOrPut(gpa, atom_ptr.output_section_index);
if (!gop.found_existing) gop.value_ptr.* = .{};
try gop.value_ptr.append(gpa, atom_ptr.atom_index);
try gop.value_ptr.append(gpa, .{ .index = atom_ptr.atom_index, .file = self.index });
}
}
@@ -1242,13 +1235,12 @@ fn updateLazySymbol(
};
const local_sym = elf_file.symbol(symbol_index);
local_sym.name_offset = name_str_index;
local_sym.output_section_index = output_section_index;
const local_esym = &self.local_esyms.items(.elf_sym)[local_sym.esym_index];
local_esym.st_name = name_str_index;
local_esym.st_info |= elf.STT_OBJECT;
local_esym.st_size = code.len;
const atom_ptr = local_sym.atom(elf_file).?;
atom_ptr.flags.alive = true;
atom_ptr.alive = true;
atom_ptr.name_offset = name_str_index;
atom_ptr.alignment = required_alignment;
atom_ptr.size = code.len;
@@ -1307,8 +1299,7 @@ pub fn lowerUnnamedConst(
return error.CodegenFail;
},
};
const sym = elf_file.symbol(sym_index);
try unnamed_consts.append(gpa, sym.atom_index);
try unnamed_consts.append(gpa, sym_index);
return sym_index;
}
@@ -1332,7 +1323,7 @@ fn lowerConst(
var code_buffer = std.ArrayList(u8).init(gpa);
defer code_buffer.deinit();
const sym_index = try self.addAtom(elf_file);
const sym_index = try self.newAtom(elf_file);
const res = try codegen.generateSymbol(
&elf_file.base,
@@ -1351,13 +1342,12 @@ fn lowerConst(
const local_sym = elf_file.symbol(sym_index);
const name_str_index = try self.strtab.insert(gpa, name);
local_sym.name_offset = name_str_index;
local_sym.output_section_index = output_section_index;
const local_esym = &self.local_esyms.items(.elf_sym)[local_sym.esym_index];
local_esym.st_name = name_str_index;
local_esym.st_info |= elf.STT_OBJECT;
local_esym.st_size = code.len;
const atom_ptr = local_sym.atom(elf_file).?;
atom_ptr.flags.alive = true;
atom_ptr.alive = true;
atom_ptr.name_offset = name_str_index;
atom_ptr.alignment = required_alignment;
atom_ptr.size = code.len;
@@ -1530,6 +1520,72 @@ pub fn getString(self: ZigObject, off: u32) [:0]const u8 {
return self.strtab.getAssumeExists(off);
}
fn addAtom(self: *ZigObject, allocator: Allocator) !Atom.Index {
try self.atoms.ensureUnusedCapacity(allocator, 1);
try self.atoms_extra.ensureUnusedCapacity(allocator, @sizeOf(Atom.Extra));
return self.addAtomAssumeCapacity();
}
fn addAtomAssumeCapacity(self: *ZigObject) Atom.Index {
const atom_index: Atom.Index = @intCast(self.atoms.items.len);
const atom_ptr = self.atoms.addOneAssumeCapacity();
atom_ptr.* = .{
.file_index = self.index,
.atom_index = atom_index,
.extra_index = self.addAtomExtraAssumeCapacity(.{}),
};
return atom_index;
}
pub fn atom(self: *ZigObject, atom_index: Atom.Index) ?*Atom {
if (atom_index == 0) return null;
assert(atom_index < self.atoms.items.len);
return &self.atoms.items[atom_index];
}
fn addAtomExtra(self: *ZigObject, allocator: Allocator, extra: Atom.Extra) !u32 {
const fields = @typeInfo(Atom.Extra).Struct.fields;
try self.atoms_extra.ensureUnusedCapacity(allocator, fields.len);
return self.addAtomExtraAssumeCapacity(extra);
}
fn addAtomExtraAssumeCapacity(self: *ZigObject, extra: Atom.Extra) u32 {
const index = @as(u32, @intCast(self.atoms_extra.items.len));
const fields = @typeInfo(Atom.Extra).Struct.fields;
inline for (fields) |field| {
self.atoms_extra.appendAssumeCapacity(switch (field.type) {
u32 => @field(extra, field.name),
else => @compileError("bad field type"),
});
}
return index;
}
pub fn atomExtra(self: ZigObject, index: u32) Atom.Extra {
const fields = @typeInfo(Atom.Extra).Struct.fields;
var i: usize = index;
var result: Atom.Extra = undefined;
inline for (fields) |field| {
@field(result, field.name) = switch (field.type) {
u32 => self.atoms_extra.items[i],
else => @compileError("bad field type"),
};
i += 1;
}
return result;
}
pub fn setAtomExtra(self: *ZigObject, index: u32, extra: Atom.Extra) void {
assert(index > 0);
const fields = @typeInfo(Atom.Extra).Struct.fields;
inline for (fields, 0..) |field, i| {
self.atoms_extra.items[index + i] = switch (field.type) {
u32 => @field(extra, field.name),
else => @compileError("bad field type"),
};
}
}
pub fn fmtSymtab(self: *ZigObject, elf_file: *Elf) std.fmt.Formatter(formatSymtab) {
return .{ .data = .{
.self = self,
@@ -1578,9 +1634,9 @@ fn formatAtoms(
_ = unused_fmt_string;
_ = options;
try writer.writeAll(" atoms\n");
for (ctx.self.atoms.items) |atom_index| {
const atom = ctx.elf_file.atom(atom_index) orelse continue;
try writer.print(" {}\n", .{atom.fmt(ctx.elf_file)});
for (ctx.self.atoms_indexes.items) |atom_index| {
const atom_ptr = ctx.self.atom(atom_index) orelse continue;
try writer.print(" {}\n", .{atom_ptr.fmt(ctx.elf_file)});
}
}

View File

@@ -42,8 +42,8 @@ pub const Fde = struct {
const object = elf_file.file(fde.file_index).?.object;
const rel = fde.relocs(elf_file)[0];
const sym = object.symtab.items[rel.r_sym()];
const atom_index = object.atoms.items[sym.st_shndx];
return elf_file.atom(atom_index).?;
const atom_index = object.atoms_indexes.items[sym.st_shndx];
return object.atom(atom_index).?;
}
pub fn relocs(fde: Fde, elf_file: *Elf) []align(1) const elf.Elf64_Rela {
@@ -421,7 +421,7 @@ fn emitReloc(elf_file: *Elf, rec: anytype, sym: *const Symbol, rel: elf.Elf64_Re
switch (sym.type(elf_file)) {
elf.STT_SECTION => {
r_addend += @intCast(sym.address(.{}, elf_file));
r_sym = elf_file.sectionSymbolOutputSymtabIndex(sym.outputShndx().?);
r_sym = elf_file.sectionSymbolOutputSymtabIndex(sym.outputShndx(elf_file).?);
},
else => {
r_sym = sym.outputSymtabIndex(elf_file) orelse 0;

View File

@@ -98,11 +98,34 @@ pub const File = union(enum) {
}
}
pub fn atom(file: File, atom_index: Atom.Index) ?*Atom {
return switch (file) {
.shared_object => unreachable,
.linker_defined => null,
inline else => |x| x.atom(atom_index),
};
}
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,
.shared_object => unreachable,
.linker_defined => &[0]Atom.Index{},
.zig_object => |x| x.atoms_indexes.items,
.object => |x| x.atoms_indexes.items,
};
}
pub fn atomExtra(file: File, extra_index: u32) Atom.Extra {
return switch (file) {
.shared_object, .linker_defined => unreachable,
inline else => |x| x.atomExtra(extra_index),
};
}
pub fn setAtomExtra(file: File, extra_index: u32, extra: Atom.Extra) void {
return switch (file) {
.shared_object, .linker_defined => unreachable,
inline else => |x| x.setAtomExtra(extra_index, extra),
};
}
@@ -114,6 +137,13 @@ pub const File = union(enum) {
};
}
pub fn comdatGroup(file: File, ind: Elf.ComdatGroup.Index) *Elf.ComdatGroup {
return switch (file) {
.linker_defined, .shared_object, .zig_object => unreachable,
.object => |x| x.comdatGroup(ind),
};
}
pub fn symbol(file: File, ind: Symbol.Index) Symbol.Index {
return switch (file) {
.zig_object => |x| x.symbol(ind),
@@ -134,6 +164,12 @@ pub const File = union(enum) {
};
}
pub fn getString(file: File, off: u32) [:0]const u8 {
return switch (file) {
inline else => |x| x.getString(off),
};
}
pub fn updateSymtabSize(file: File, elf_file: *Elf) !void {
return switch (file) {
inline else => |x| x.updateSymtabSize(elf_file),

View File

@@ -16,9 +16,11 @@ pub fn gcAtoms(elf_file: *Elf) !void {
}
fn collectRoots(roots: *std.ArrayList(*Atom), files: []const File.Index, elf_file: *Elf) !void {
if (elf_file.entry_index) |index| {
const global = elf_file.symbol(index);
try markSymbol(global, roots, elf_file);
if (elf_file.linkerDefinedPtr()) |obj| {
if (obj.entry_index) |index| {
const global = elf_file.symbol(index);
try markSymbol(global, roots, elf_file);
}
}
for (files) |index| {
@@ -35,8 +37,8 @@ fn collectRoots(roots: *std.ArrayList(*Atom), files: []const File.Index, elf_fil
const file = elf_file.file(index).?;
for (file.atoms()) |atom_index| {
const atom = elf_file.atom(atom_index) orelse continue;
if (!atom.flags.alive) continue;
const atom = file.atom(atom_index) orelse continue;
if (!atom.alive) continue;
const shdr = atom.inputShdr(elf_file);
const name = atom.name(elf_file);
@@ -54,7 +56,7 @@ fn collectRoots(roots: *std.ArrayList(*Atom), files: []const File.Index, elf_fil
break :blk false;
};
if (is_gc_root and markAtom(atom)) try roots.append(atom);
if (shdr.sh_flags & elf.SHF_ALLOC == 0) atom.flags.visited = true;
if (shdr.sh_flags & elf.SHF_ALLOC == 0) atom.visited = true;
}
// Mark every atom referenced by CIE as alive.
@@ -77,22 +79,22 @@ fn markSymbol(sym: *Symbol, roots: *std.ArrayList(*Atom), elf_file: *Elf) !void
}
fn markAtom(atom: *Atom) bool {
const already_visited = atom.flags.visited;
atom.flags.visited = true;
return atom.flags.alive and !already_visited;
const already_visited = atom.visited;
atom.visited = true;
return atom.alive and !already_visited;
}
fn markLive(atom: *Atom, elf_file: *Elf) void {
if (@import("build_options").enable_logging) track_live_level.incr();
assert(atom.flags.visited);
assert(atom.visited);
const file = atom.file(elf_file).?;
for (atom.fdes(elf_file)) |fde| {
for (fde.relocs(elf_file)[1..]) |rel| {
const target_sym = elf_file.symbol(file.symbol(rel.r_sym()));
const target_atom = target_sym.atom(elf_file) orelse continue;
target_atom.flags.alive = true;
target_atom.alive = true;
gc_track_live_log.debug("{}marking live atom({d})", .{ track_live_level, target_atom.atom_index });
if (markAtom(target_atom)) markLive(target_atom, elf_file);
}
@@ -105,7 +107,7 @@ fn markLive(atom: *Atom, elf_file: *Elf) void {
continue;
}
const target_atom = target_sym.atom(elf_file) orelse continue;
target_atom.flags.alive = true;
target_atom.alive = true;
gc_track_live_log.debug("{}marking live atom({d})", .{ track_live_level, target_atom.atom_index });
if (markAtom(target_atom)) markLive(target_atom, elf_file);
}
@@ -120,10 +122,11 @@ fn mark(roots: std.ArrayList(*Atom), elf_file: *Elf) void {
fn prune(files: []const File.Index, elf_file: *Elf) void {
for (files) |index| {
for (elf_file.file(index).?.atoms()) |atom_index| {
const atom = elf_file.atom(atom_index) orelse continue;
if (atom.flags.alive and !atom.flags.visited) {
atom.flags.alive = false;
const file = elf_file.file(index).?;
for (file.atoms()) |atom_index| {
const atom = file.atom(atom_index) orelse continue;
if (atom.alive and !atom.visited) {
atom.alive = false;
atom.markFdesDead(elf_file);
}
}
@@ -133,9 +136,10 @@ fn prune(files: []const File.Index, elf_file: *Elf) void {
pub fn dumpPrunedAtoms(elf_file: *Elf) !void {
const stderr = std.io.getStdErr().writer();
for (elf_file.objects.items) |index| {
for (elf_file.file(index).?.object.atoms.items) |atom_index| {
const atom = elf_file.atom(atom_index) orelse continue;
if (!atom.flags.alive)
const file = elf_file.file(index).?;
for (file.atoms()) |atom_index| {
const atom = file.atom(atom_index) orelse continue;
if (!atom.alive)
// TODO should we simply print to stderr?
try stderr.print("link: removing unused section '{s}' in file '{}'\n", .{
atom.name(elf_file),

View File

@@ -10,16 +10,18 @@ pub const MergeSection = struct {
IndexContext,
std.hash_map.default_max_load_percentage,
) = .{},
subsections: std.ArrayListUnmanaged(MergeSubsection.Index) = .{},
subsections: std.ArrayListUnmanaged(MergeSubsection) = .{},
finalized_subsections: std.ArrayListUnmanaged(MergeSubsection.Index) = .{},
pub fn deinit(msec: *MergeSection, allocator: Allocator) void {
msec.bytes.deinit(allocator);
msec.table.deinit(allocator);
msec.subsections.deinit(allocator);
msec.finalized_subsections.deinit(allocator);
}
pub fn name(msec: MergeSection, elf_file: *Elf) [:0]const u8 {
return elf_file.strings.getAssumeExists(msec.name_offset);
return elf_file.getShString(msec.name_offset);
}
pub fn address(msec: MergeSection, elf_file: *Elf) i64 {
@@ -58,25 +60,26 @@ pub const MergeSection = struct {
/// Finalizes the merge section and clears hash table.
/// Sorts all owned subsections.
pub fn finalize(msec: *MergeSection, elf_file: *Elf) !void {
const gpa = elf_file.base.comp.gpa;
try msec.subsections.ensureTotalCapacityPrecise(gpa, msec.table.count());
pub fn finalize(msec: *MergeSection, allocator: Allocator) !void {
try msec.finalized_subsections.ensureTotalCapacityPrecise(allocator, msec.subsections.items.len);
var it = msec.table.iterator();
while (it.next()) |entry| {
const msub = elf_file.mergeSubsection(entry.value_ptr.*);
const msub = msec.mergeSubsection(entry.value_ptr.*);
if (!msub.alive) continue;
msec.subsections.appendAssumeCapacity(entry.value_ptr.*);
msec.finalized_subsections.appendAssumeCapacity(entry.value_ptr.*);
}
msec.table.clearAndFree(gpa);
msec.table.clearAndFree(allocator);
const sortFn = struct {
pub fn sortFn(ctx: *Elf, lhs: MergeSubsection.Index, rhs: MergeSubsection.Index) bool {
pub fn sortFn(ctx: *MergeSection, lhs: MergeSubsection.Index, rhs: MergeSubsection.Index) bool {
const lhs_msub = ctx.mergeSubsection(lhs);
const rhs_msub = ctx.mergeSubsection(rhs);
if (lhs_msub.alignment.compareStrict(.eq, rhs_msub.alignment)) {
if (lhs_msub.size == rhs_msub.size) {
return mem.order(u8, lhs_msub.getString(ctx), rhs_msub.getString(ctx)) == .lt;
const lhs_string = ctx.bytes.items[lhs_msub.string_index..][0..lhs_msub.size];
const rhs_string = ctx.bytes.items[rhs_msub.string_index..][0..rhs_msub.size];
return mem.order(u8, lhs_string, rhs_string) == .lt;
}
return lhs_msub.size < rhs_msub.size;
}
@@ -84,7 +87,19 @@ pub const MergeSection = struct {
}
}.sortFn;
std.mem.sort(MergeSubsection.Index, msec.subsections.items, elf_file, sortFn);
std.mem.sort(MergeSubsection.Index, msec.finalized_subsections.items, msec, sortFn);
}
pub fn addMergeSubsection(msec: *MergeSection, allocator: Allocator) !MergeSubsection.Index {
const index: MergeSubsection.Index = @intCast(msec.subsections.items.len);
const msub = try msec.subsections.addOne(allocator);
msub.* = .{};
return index;
}
pub fn mergeSubsection(msec: *MergeSection, index: MergeSubsection.Index) *MergeSubsection {
assert(index < msec.subsections.items.len);
return &msec.subsections.items[index];
}
pub const IndexContext = struct {
@@ -154,8 +169,8 @@ pub const MergeSection = struct {
msec.type,
msec.flags,
});
for (msec.subsections.items) |index| {
try writer.print(" {}\n", .{elf_file.mergeSubsection(index).fmt(elf_file)});
for (msec.subsections.items) |msub| {
try writer.print(" {}\n", .{msub.fmt(elf_file)});
}
}
@@ -250,18 +265,26 @@ pub const InputMergeSection = struct {
// TODO: imsec.strings.clearAndFree(allocator);
}
pub fn findSubsection(imsec: InputMergeSection, offset: u32) ?struct { MergeSubsection.Index, u32 } {
const FindSubsectionResult = struct {
msub_index: MergeSubsection.Index,
offset: u32,
};
pub fn findSubsection(imsec: InputMergeSection, offset: u32) ?FindSubsectionResult {
// TODO: binary search
for (imsec.offsets.items, 0..) |off, index| {
if (offset < off) return .{
imsec.subsections.items[index - 1],
offset - imsec.offsets.items[index - 1],
.msub_index = imsec.subsections.items[index - 1],
.offset = offset - imsec.offsets.items[index - 1],
};
}
const last = imsec.offsets.items.len - 1;
const last_off = imsec.offsets.items[last];
const last_len = imsec.strings.items[last].len;
if (offset < last_off + last_len) return .{ imsec.subsections.items[last], offset - last_off };
if (offset < last_off + last_len) return .{
.msub_index = imsec.subsections.items[last],
.offset = offset - last_off,
};
return null;
}

View File

@@ -190,7 +190,7 @@ pub fn flushObject(elf_file: *Elf, comp: *Compilation, module_obj_path: ?[]const
// Now, we are ready to resolve the symbols across all input files.
// We will first resolve the files in the ZigObject, next in the parsed
// input Object files.
elf_file.resolveSymbols();
try elf_file.resolveSymbols();
elf_file.markEhFrameAtomsDead();
try elf_file.resolveMergeSections();
try elf_file.addCommentString();
@@ -299,13 +299,16 @@ fn initSections(elf_file: *Elf) !void {
} else false;
if (needs_eh_frame) {
elf_file.eh_frame_section_index = try elf_file.addSection(.{
.name = ".eh_frame",
.name = try elf_file.insertShString(".eh_frame"),
.type = elf.SHT_PROGBITS,
.flags = elf.SHF_ALLOC,
.addralign = ptr_size,
.offset = std.math.maxInt(u64),
});
elf_file.eh_frame_rela_section_index = try elf_file.addRelaShdr(".rela.eh_frame", elf_file.eh_frame_section_index.?);
elf_file.eh_frame_rela_section_index = try elf_file.addRelaShdr(
try elf_file.insertShString(".rela.eh_frame"),
elf_file.eh_frame_section_index.?,
);
}
try initComdatGroups(elf_file);
@@ -318,22 +321,18 @@ fn initComdatGroups(elf_file: *Elf) !void {
for (elf_file.objects.items) |index| {
const object = elf_file.file(index).?.object;
for (object.comdat_groups.items) |cg_index| {
const cg = elf_file.comdatGroup(cg_index);
const cg_owner = elf_file.comdatGroupOwner(cg.owner);
if (cg_owner.file != index) continue;
for (object.comdat_groups.items, 0..) |cg, cg_index| {
if (!cg.alive) continue;
const cg_sec = try elf_file.comdat_group_sections.addOne(gpa);
cg_sec.* = .{
.shndx = try elf_file.addSection(.{
.name = ".group",
.name = try elf_file.insertShString(".group"),
.type = elf.SHT_GROUP,
.entsize = @sizeOf(u32),
.addralign = @alignOf(u32),
.offset = std.math.maxInt(u64),
}),
.cg_index = cg_index,
.cg_ref = .{ .index = @intCast(cg_index), .file = index },
};
}
}
@@ -342,9 +341,9 @@ fn initComdatGroups(elf_file: *Elf) !void {
fn updateSectionSizes(elf_file: *Elf) !void {
for (elf_file.output_sections.keys(), elf_file.output_sections.values()) |shndx, atom_list| {
const shdr = &elf_file.shdrs.items[shndx];
for (atom_list.items) |atom_index| {
const atom_ptr = elf_file.atom(atom_index) orelse continue;
if (!atom_ptr.flags.alive) continue;
for (atom_list.items) |ref| {
const atom_ptr = elf_file.atom(ref) orelse continue;
if (!atom_ptr.alive) continue;
const offset = atom_ptr.alignment.forward(shdr.sh_size);
const padding = offset - shdr.sh_size;
atom_ptr.value = @intCast(offset);
@@ -355,9 +354,9 @@ fn updateSectionSizes(elf_file: *Elf) !void {
for (elf_file.output_rela_sections.values()) |sec| {
const shdr = &elf_file.shdrs.items[sec.shndx];
for (sec.atom_list.items) |atom_index| {
const atom_ptr = elf_file.atom(atom_index) orelse continue;
if (!atom_ptr.flags.alive) continue;
for (sec.atom_list.items) |ref| {
const atom_ptr = elf_file.atom(ref) orelse continue;
if (!atom_ptr.alive) continue;
const relocs = atom_ptr.relocs(elf_file);
shdr.sh_size += shdr.sh_entsize * relocs.len;
}
@@ -386,7 +385,7 @@ fn updateComdatGroupsSizes(elf_file: *Elf) void {
const sym = elf_file.symbol(cg.symbol(elf_file));
shdr.sh_info = sym.outputSymtabIndex(elf_file) orelse
elf_file.sectionSymbolOutputSymtabIndex(sym.outputShndx().?);
elf_file.sectionSymbolOutputSymtabIndex(sym.outputShndx(elf_file).?);
}
}
@@ -449,16 +448,16 @@ fn writeAtoms(elf_file: *Elf) !void {
0;
@memset(buffer, padding_byte);
for (atom_list.items) |atom_index| {
const atom_ptr = elf_file.atom(atom_index).?;
assert(atom_ptr.flags.alive);
for (atom_list.items) |ref| {
const atom_ptr = elf_file.atom(ref).?;
assert(atom_ptr.alive);
const offset = math.cast(usize, atom_ptr.value - @as(i64, @intCast(shdr.sh_addr - base_offset))) orelse
return error.Overflow;
const size = math.cast(usize, atom_ptr.size) orelse return error.Overflow;
log.debug("writing atom({d}) from 0x{x} to 0x{x}", .{
atom_index,
log.debug("writing atom({}) from 0x{x} to 0x{x}", .{
ref,
sh_offset + offset,
sh_offset + offset + size,
});
@@ -466,8 +465,8 @@ fn writeAtoms(elf_file: *Elf) !void {
// TODO decompress directly into provided buffer
const out_code = buffer[offset..][0..size];
const in_code = switch (atom_ptr.file(elf_file).?) {
.object => |x| try x.codeDecompressAlloc(elf_file, atom_index),
.zig_object => |x| try x.codeAlloc(elf_file, atom_index),
.object => |x| try x.codeDecompressAlloc(elf_file, ref.index),
.zig_object => |x| try x.codeAlloc(elf_file, ref.index),
else => unreachable,
};
defer gpa.free(in_code);
@@ -491,9 +490,9 @@ fn writeSyntheticSections(elf_file: *Elf) !void {
var relocs = try std.ArrayList(elf.Elf64_Rela).initCapacity(gpa, num_relocs);
defer relocs.deinit();
for (sec.atom_list.items) |atom_index| {
const atom_ptr = elf_file.atom(atom_index) orelse continue;
if (!atom_ptr.flags.alive) continue;
for (sec.atom_list.items) |ref| {
const atom_ptr = elf_file.atom(ref) orelse continue;
if (!atom_ptr.alive) continue;
try atom_ptr.writeRelocs(elf_file, &relocs);
}
assert(relocs.items.len == num_relocs);

View File

@@ -1075,7 +1075,7 @@ pub const GotPltSection = struct {
_ = got_plt;
{
// [0]: _DYNAMIC
const symbol = elf_file.symbol(elf_file.dynamic_index.?);
const symbol = elf_file.symbol(elf_file.linkerDefinedPtr().?.dynamic_index.?);
try writer.writeInt(u64, @intCast(symbol.address(.{}, elf_file)), .little);
}
// [1]: 0x0
@@ -1670,45 +1670,44 @@ pub const VerneedSection = struct {
pub const ComdatGroupSection = struct {
shndx: u32,
cg_index: u32,
cg_ref: Elf.Ref,
fn file(cgs: ComdatGroupSection, elf_file: *Elf) ?File {
const cg = elf_file.comdatGroup(cgs.cg_index);
const cg_owner = elf_file.comdatGroupOwner(cg.owner);
return elf_file.file(cg_owner.file);
fn comdatGroup(cgs: ComdatGroupSection, elf_file: *Elf) *Elf.ComdatGroup {
const cg_file = elf_file.file(cgs.cg_ref.file).?;
return cg_file.object.comdatGroup(cgs.cg_ref.index);
}
pub fn symbol(cgs: ComdatGroupSection, elf_file: *Elf) Symbol.Index {
const cg = elf_file.comdatGroup(cgs.cg_index);
const object = cgs.file(elf_file).?.object;
const cg = cgs.comdatGroup(elf_file);
const object = cg.file(elf_file).object;
const shdr = object.shdrs.items[cg.shndx];
return object.symbols.items[shdr.sh_info];
}
pub fn size(cgs: ComdatGroupSection, elf_file: *Elf) usize {
const cg = elf_file.comdatGroup(cgs.cg_index);
const cg = cgs.comdatGroup(elf_file);
const members = cg.comdatGroupMembers(elf_file);
return (members.len + 1) * @sizeOf(u32);
}
pub fn write(cgs: ComdatGroupSection, elf_file: *Elf, writer: anytype) !void {
const cg = elf_file.comdatGroup(cgs.cg_index);
const object = cgs.file(elf_file).?.object;
const cg = cgs.comdatGroup(elf_file);
const object = cg.file(elf_file).object;
const members = cg.comdatGroupMembers(elf_file);
try writer.writeInt(u32, elf.GRP_COMDAT, .little);
for (members) |shndx| {
const shdr = object.shdrs.items[shndx];
switch (shdr.sh_type) {
elf.SHT_RELA => {
const atom_index = object.atoms.items[shdr.sh_info];
const atom = elf_file.atom(atom_index).?;
const rela = elf_file.output_rela_sections.get(atom.outputShndx().?).?;
const atom_index = object.atoms_indexes.items[shdr.sh_info];
const atom = object.atom(atom_index).?;
const rela = elf_file.output_rela_sections.get(atom.output_section_index).?;
try writer.writeInt(u32, rela.shndx, .little);
},
else => {
const atom_index = object.atoms.items[shndx];
const atom = elf_file.atom(atom_index).?;
try writer.writeInt(u32, atom.outputShndx().?, .little);
const atom_index = object.atoms_indexes.items[shndx];
const atom = object.atom(atom_index).?;
try writer.writeInt(u32, atom.output_section_index, .little);
},
}
}

View File

@@ -6,22 +6,21 @@ pub fn createThunks(shndx: u32, elf_file: *Elf) !void {
const atoms = elf_file.output_sections.get(shndx).?.items;
assert(atoms.len > 0);
for (atoms) |atom_index| {
elf_file.atom(atom_index).?.value = -1;
for (atoms) |ref| {
elf_file.atom(ref).?.value = -1;
}
var i: usize = 0;
while (i < atoms.len) {
const start = i;
const start_atom = elf_file.atom(atoms[start]).?;
assert(start_atom.flags.alive);
assert(start_atom.alive);
start_atom.value = try advance(shdr, start_atom.size, start_atom.alignment);
i += 1;
while (i < atoms.len) : (i += 1) {
const atom_index = atoms[i];
const atom = elf_file.atom(atom_index).?;
assert(atom.flags.alive);
const atom = elf_file.atom(atoms[i]).?;
assert(atom.alive);
if (@as(i64, @intCast(atom.alignment.forward(shdr.sh_size))) - start_atom.value >= max_distance)
break;
atom.value = try advance(shdr, atom.size, atom.alignment);
@@ -33,10 +32,10 @@ pub fn createThunks(shndx: u32, elf_file: *Elf) !void {
thunk.output_section_index = shndx;
// Scan relocs in the group and create trampolines for any unreachable callsite
for (atoms[start..i]) |atom_index| {
const atom = elf_file.atom(atom_index).?;
for (atoms[start..i]) |ref| {
const atom = elf_file.atom(ref).?;
const file = atom.file(elf_file).?;
log.debug("atom({d}) {s}", .{ atom_index, atom.name(elf_file) });
log.debug("atom({}) {s}", .{ ref, atom.name(elf_file) });
for (atom.relocs(elf_file)) |rel| {
const is_reachable = switch (cpu_arch) {
.aarch64 => aarch64.isReachable(atom, rel, elf_file),
@@ -51,8 +50,7 @@ pub fn createThunks(shndx: u32, elf_file: *Elf) !void {
};
try thunk.symbols.put(gpa, target, {});
}
try atom.addExtra(.{ .thunk = thunk_index }, elf_file);
atom.flags.thunk = true;
atom.addExtra(.{ .thunk = thunk_index }, elf_file);
}
thunk.value = try advance(shdr, thunk.size(elf_file), Atom.Alignment.fromNonzeroByteUnits(2));

View File

@@ -45,8 +45,7 @@ pub fn deinit(self: *InternalObject, allocator: Allocator) void {
pub fn init(self: *InternalObject, allocator: Allocator) !void {
// Atom at index 0 is reserved as null atom.
try self.atoms.append(allocator, .{});
try self.atoms_extra.append(allocator, 0);
try self.atoms.append(allocator, .{ .extra = try self.addAtomExtra(allocator, .{}) });
// Null byte in strtab
try self.strtab.append(allocator, 0);
}

View File

@@ -1634,7 +1634,7 @@ fn isThreadlocal(macho_file: *MachO, decl_index: InternPool.DeclIndex) bool {
fn addAtom(self: *ZigObject, allocator: Allocator) !Atom.Index {
try self.atoms.ensureUnusedCapacity(allocator, 1);
try self.atoms_extra.ensureUnusedCapacity(allocator, 1);
try self.atoms_extra.ensureUnusedCapacity(allocator, @sizeOf(Atom.Extra));
return self.addAtomAssumeCapacity();
}

View File

@@ -59,6 +59,7 @@ pub fn testAll(b: *Build, build_opts: BuildOptions) *Step {
// Exercise linker with LLVM backend
// musl tests
elf_step.dependOn(testAbsSymbols(b, .{ .target = musl_target }));
elf_step.dependOn(testComdatElimination(b, .{ .target = musl_target }));
elf_step.dependOn(testCommonSymbols(b, .{ .target = musl_target }));
elf_step.dependOn(testCommonSymbolsInArchive(b, .{ .target = musl_target }));
elf_step.dependOn(testCommentString(b, .{ .target = musl_target }));
@@ -368,6 +369,109 @@ fn testCanonicalPlt(b: *Build, opts: Options) *Step {
return test_step;
}
fn testComdatElimination(b: *Build, opts: Options) *Step {
const test_step = addTestStep(b, "comdat-elimination", opts);
const a_o = addObject(b, opts, .{
.name = "a",
.cpp_source_bytes =
\\#include <stdio.h>
\\inline void foo() {
\\ printf("calling foo in a\n");
\\}
\\void hello() {
\\ foo();
\\}
,
});
a_o.linkLibCpp();
const main_o = addObject(b, opts, .{
.name = "main",
.cpp_source_bytes =
\\#include <stdio.h>
\\inline void foo() {
\\ printf("calling foo in main\n");
\\}
\\void hello();
\\int main() {
\\ foo();
\\ hello();
\\ return 0;
\\}
,
});
main_o.linkLibCpp();
{
const exe = addExecutable(b, opts, .{ .name = "main1" });
exe.addObject(a_o);
exe.addObject(main_o);
exe.linkLibCpp();
const run = addRunArtifact(exe);
run.expectStdOutEqual(
\\calling foo in a
\\calling foo in a
\\
);
test_step.dependOn(&run.step);
}
{
const exe = addExecutable(b, opts, .{ .name = "main2" });
exe.addObject(main_o);
exe.addObject(a_o);
exe.linkLibCpp();
const run = addRunArtifact(exe);
run.expectStdOutEqual(
\\calling foo in main
\\calling foo in main
\\
);
test_step.dependOn(&run.step);
}
{
const c_o = addObject(b, opts, .{ .name = "c" });
c_o.addObject(main_o);
c_o.addObject(a_o);
const exe = addExecutable(b, opts, .{ .name = "main3" });
exe.addObject(c_o);
exe.linkLibCpp();
const run = addRunArtifact(exe);
run.expectStdOutEqual(
\\calling foo in main
\\calling foo in main
\\
);
test_step.dependOn(&run.step);
}
{
const d_o = addObject(b, opts, .{ .name = "d" });
d_o.addObject(a_o);
d_o.addObject(main_o);
const exe = addExecutable(b, opts, .{ .name = "main4" });
exe.addObject(d_o);
exe.linkLibCpp();
const run = addRunArtifact(exe);
run.expectStdOutEqual(
\\calling foo in a
\\calling foo in a
\\
);
test_step.dependOn(&run.step);
}
return test_step;
}
fn testCommentString(b: *Build, opts: Options) *Step {
const test_step = addTestStep(b, "comment-string", opts);