elf: rename SectionChunk into AtomList and store as part of Section

This commit is contained in:
Jakub Konka
2024-09-04 11:40:56 +02:00
parent 64ad6eff16
commit d302a1068e
3 changed files with 103 additions and 326 deletions

View File

@@ -393,7 +393,8 @@ pub fn deinit(self: *Elf) void {
self.objects.deinit(gpa);
self.shared_objects.deinit(gpa);
for (self.sections.items(.atom_list), self.sections.items(.free_list)) |*atoms, *free_list| {
for (self.sections.items(.atom_list_2), self.sections.items(.atom_list), self.sections.items(.free_list)) |*atom_list, *atoms, *free_list| {
atom_list.deinit(gpa);
atoms.deinit(gpa);
free_list.deinit(gpa);
}
@@ -3204,8 +3205,9 @@ fn sortInitFini(self: *Elf) !void {
}
};
for (slice.items(.shdr), slice.items(.atom_list)) |shdr, *atom_list| {
for (slice.items(.shdr), slice.items(.atom_list_2)) |shdr, *atom_list| {
if (shdr.sh_flags & elf.SHF_ALLOC == 0) continue;
if (atom_list.atoms.items.len == 0) continue;
var is_init_fini = false;
var is_ctor_dtor = false;
@@ -3219,15 +3221,13 @@ fn sortInitFini(self: *Elf) !void {
is_ctor_dtor = mem.indexOf(u8, name, ".ctors") != null or mem.indexOf(u8, name, ".dtors") != null;
},
}
if (!is_init_fini and !is_ctor_dtor) continue;
if (atom_list.items.len == 0) continue;
var entries = std.ArrayList(Entry).init(gpa);
try entries.ensureTotalCapacityPrecise(atom_list.items.len);
try entries.ensureTotalCapacityPrecise(atom_list.atoms.items.len);
defer entries.deinit();
for (atom_list.items) |ref| {
for (atom_list.atoms.items) |ref| {
const atom_ptr = self.atom(ref).?;
const object = atom_ptr.file(self).?.object;
const priority = blk: {
@@ -3246,9 +3246,9 @@ fn sortInitFini(self: *Elf) !void {
mem.sort(Entry, entries.items, self, Entry.lessThan);
atom_list.clearRetainingCapacity();
atom_list.atoms.clearRetainingCapacity();
for (entries.items) |entry| {
atom_list.appendAssumeCapacity(entry.atom_ref);
atom_list.atoms.appendAssumeCapacity(entry.atom_ref);
}
}
}
@@ -3491,13 +3491,19 @@ fn resetShdrIndexes(self: *Elf, backlinks: []const u32) void {
msec.output_section_index = backlinks[msec.output_section_index];
}
for (self.sections.items(.shdr)) |*shdr| {
if (shdr.sh_type != elf.SHT_RELA) continue;
// FIXME:JK we should spin up .symtab potentially earlier, or set all non-dynamic RELA sections
// to point at symtab
// shdr.sh_link = backlinks[shdr.sh_link];
shdr.sh_link = self.symtab_section_index.?;
shdr.sh_info = backlinks[shdr.sh_info];
const slice = self.sections.slice();
for (slice.items(.shdr), slice.items(.atom_list_2)) |*shdr, *atom_list| {
atom_list.output_section_index = backlinks[atom_list.output_section_index];
for (atom_list.atoms.items) |ref| {
self.atom(ref).?.output_section_index = atom_list.output_section_index;
}
if (shdr.sh_type == elf.SHT_RELA) {
// FIXME:JK we should spin up .symtab potentially earlier, or set all non-dynamic RELA sections
// to point at symtab
// shdr.sh_link = backlinks[shdr.sh_link];
shdr.sh_link = self.symtab_section_index.?;
shdr.sh_info = backlinks[shdr.sh_info];
}
}
if (self.zigObjectPtr()) |zo| {
@@ -3507,79 +3513,71 @@ fn resetShdrIndexes(self: *Elf, backlinks: []const u32) void {
}
}
for (self.objects.items) |index| {
const object = self.file(index).?.object;
for (object.section_chunks.items) |*chunk| {
chunk.output_section_index = backlinks[chunk.output_section_index];
for (chunk.atoms.items) |atom_index| {
object.atom(atom_index).?.output_section_index = chunk.output_section_index;
}
}
}
for (self.comdat_group_sections.items) |*cg| {
cg.shndx = backlinks[cg.shndx];
}
if (self.symtab_section_index) |index| {
const shdr = &self.sections.items(.shdr)[index];
const shdr = &slice.items(.shdr)[index];
shdr.sh_link = self.strtab_section_index.?;
}
if (self.dynamic_section_index) |index| {
const shdr = &self.sections.items(.shdr)[index];
const shdr = &slice.items(.shdr)[index];
shdr.sh_link = self.dynstrtab_section_index.?;
}
if (self.dynsymtab_section_index) |index| {
const shdr = &self.sections.items(.shdr)[index];
const shdr = &slice.items(.shdr)[index];
shdr.sh_link = self.dynstrtab_section_index.?;
}
if (self.hash_section_index) |index| {
const shdr = &self.sections.items(.shdr)[index];
const shdr = &slice.items(.shdr)[index];
shdr.sh_link = self.dynsymtab_section_index.?;
}
if (self.gnu_hash_section_index) |index| {
const shdr = &self.sections.items(.shdr)[index];
const shdr = &slice.items(.shdr)[index];
shdr.sh_link = self.dynsymtab_section_index.?;
}
if (self.versym_section_index) |index| {
const shdr = &self.sections.items(.shdr)[index];
const shdr = &slice.items(.shdr)[index];
shdr.sh_link = self.dynsymtab_section_index.?;
}
if (self.verneed_section_index) |index| {
const shdr = &self.sections.items(.shdr)[index];
const shdr = &slice.items(.shdr)[index];
shdr.sh_link = self.dynstrtab_section_index.?;
}
if (self.rela_dyn_section_index) |index| {
const shdr = &self.sections.items(.shdr)[index];
const shdr = &slice.items(.shdr)[index];
shdr.sh_link = self.dynsymtab_section_index orelse 0;
}
if (self.rela_plt_section_index) |index| {
const shdr = &self.sections.items(.shdr)[index];
const shdr = &slice.items(.shdr)[index];
shdr.sh_link = self.dynsymtab_section_index.?;
shdr.sh_info = self.plt_section_index.?;
}
if (self.eh_frame_rela_section_index) |index| {
const shdr = &self.sections.items(.shdr)[index];
const shdr = &slice.items(.shdr)[index];
shdr.sh_link = self.symtab_section_index.?;
shdr.sh_info = self.eh_frame_section_index.?;
}
}
fn updateSectionSizes(self: *Elf) !void {
for (self.objects.items) |index| {
try self.file(index).?.object.allocateAtoms(self);
const slice = self.sections.slice();
for (slice.items(.atom_list_2)) |*atom_list| {
if (atom_list.atoms.items.len == 0) continue;
atom_list.updateSize(self);
try atom_list.allocate(self);
}
const slice = self.sections.slice();
if (self.requiresThunks()) {
for (slice.items(.shdr), slice.items(.atom_list), 0..) |*shdr, atom_list, shndx| {
if (shdr.sh_flags & elf.SHF_EXECINSTR == 0) continue;
@@ -4033,15 +4031,38 @@ fn allocateSpecialPhdrs(self: *Elf) void {
}
fn writeAtoms(self: *Elf) !void {
for (self.objects.items) |index| {
try self.file(index).?.object.writeAtoms(self);
const gpa = self.base.comp.gpa;
var undefs = std.AutoArrayHashMap(SymbolResolver.Index, std.ArrayList(Ref)).init(gpa);
defer {
for (undefs.values()) |*refs| {
refs.deinit();
}
undefs.deinit();
}
if (self.requiresThunks()) {
const gpa = self.base.comp.gpa;
var buffer = std.ArrayList(u8).init(gpa);
defer buffer.deinit();
var buffer = std.ArrayList(u8).init(gpa);
defer buffer.deinit();
const slice = self.sections.slice();
var has_reloc_errors = false;
for (slice.items(.shdr), slice.items(.atom_list_2)) |shdr, atom_list| {
if (shdr.sh_type == elf.SHT_NOBITS) continue;
if (atom_list.atoms.items.len == 0) continue;
atom_list.write(&buffer, &undefs, self) catch |err| switch (err) {
error.UnsupportedCpuArch => {
try self.reportUnsupportedCpuArch();
return error.FlushFailure;
},
error.RelocFailure, error.RelaxFailure => has_reloc_errors = true,
else => |e| return e,
};
}
try self.reportUndefinedSymbols(&undefs);
if (has_reloc_errors) return error.FlushFailure;
if (self.requiresThunks()) {
for (self.thunks.items) |th| {
const thunk_size = th.size(self);
try buffer.ensureUnusedCapacity(thunk_size);
@@ -4993,7 +5014,7 @@ pub fn insertDynString(self: *Elf, name: []const u8) error{OutOfMemory}!u32 {
return off;
}
pub fn reportUndefinedSymbols(self: *Elf, undefs: anytype) !void {
fn reportUndefinedSymbols(self: *Elf, undefs: anytype) !void {
const gpa = self.base.comp.gpa;
const max_notes = 4;
@@ -5061,7 +5082,7 @@ fn reportMissingLibraryError(
}
}
pub fn reportUnsupportedCpuArch(self: *Elf) error{OutOfMemory}!void {
fn reportUnsupportedCpuArch(self: *Elf) error{OutOfMemory}!void {
var err = try self.base.addErrorWithNotes(0);
try err.addMsg("fatal linker error: unsupported CPU architecture {s}", .{
@tagName(self.getTarget().cpu.arch),
@@ -5248,9 +5269,8 @@ fn fmtDumpState(
try writer.print("object({d}) : {}", .{ index, object.fmtPath() });
if (!object.alive) try writer.writeAll(" : [*]");
try writer.writeByte('\n');
try writer.print("{}{}{}{}{}{}\n", .{
try writer.print("{}{}{}{}{}\n", .{
object.fmtAtoms(self),
object.fmtSectionChunks(self),
object.fmtCies(self),
object.fmtFdes(self),
object.fmtSymtab(self),
@@ -5274,6 +5294,14 @@ fn fmtDumpState(
try writer.print("{}\n", .{linker_defined.fmtSymtab(self)});
}
const slice = self.sections.slice();
{
try writer.writeAll("atom lists\n");
for (slice.items(.shdr), slice.items(.atom_list_2), 0..) |shdr, atom_list, shndx| {
try writer.print("shdr({d}) : {s} : {}", .{ shndx, self.getShString(shdr.sh_name), atom_list.fmt(self) });
}
}
if (self.requiresThunks()) {
try writer.writeAll("thunks\n");
for (self.thunks.items, 0..) |th, index| {
@@ -5295,7 +5323,7 @@ fn fmtDumpState(
}
try writer.writeAll("\nOutput shdrs\n");
for (self.sections.items(.shdr), self.sections.items(.phndx), 0..) |shdr, phndx, shndx| {
for (slice.items(.shdr), slice.items(.phndx), 0..) |shdr, phndx, shndx| {
try writer.print(" shdr({d}) : phdr({?d}) : {}\n", .{
shndx,
phndx,
@@ -5549,8 +5577,14 @@ const Section = struct {
phndx: ?u32 = null,
/// List of atoms contributing to this section.
/// TODO currently this is only used for relocations tracking in relocatable mode
/// but will be merged with atom_list_2.
atom_list: std.ArrayListUnmanaged(Ref) = .{},
/// List of atoms contributing to this section.
/// This can be used by sections that require special handling such as init/fini array, etc.
atom_list_2: AtomList = .{},
/// Index of the last allocated atom in this section.
last_atom: Ref = .{ .index = 0, .file = 0 },
@@ -5690,6 +5724,7 @@ const Air = @import("../Air.zig");
const Allocator = std.mem.Allocator;
const Archive = @import("Elf/Archive.zig");
pub const Atom = @import("Elf/Atom.zig");
const AtomList = @import("Elf/AtomList.zig");
const Cache = std.Build.Cache;
const Path = Cache.Path;
const Compilation = @import("../Compilation.zig");

View File

@@ -17,7 +17,6 @@ relocs: std.ArrayListUnmanaged(elf.Elf64_Rela) = .{},
atoms: std.ArrayListUnmanaged(Atom) = .{},
atoms_indexes: std.ArrayListUnmanaged(Atom.Index) = .{},
atoms_extra: std.ArrayListUnmanaged(u32) = .{},
section_chunks: std.ArrayListUnmanaged(SectionChunk) = .{},
comdat_groups: std.ArrayListUnmanaged(Elf.ComdatGroup) = .{},
comdat_group_data: std.ArrayListUnmanaged(u32) = .{},
@@ -59,10 +58,6 @@ pub fn deinit(self: *Object, allocator: Allocator) void {
self.atoms.deinit(allocator);
self.atoms_indexes.deinit(allocator);
self.atoms_extra.deinit(allocator);
for (self.section_chunks.items) |*chunk| {
chunk.deinit(allocator);
}
self.section_chunks.deinit(allocator);
self.comdat_groups.deinit(allocator);
self.comdat_group_data.deinit(allocator);
self.relocs.deinit(allocator);
@@ -952,166 +947,9 @@ pub fn initOutputSections(self: *Object, elf_file: *Elf) !void {
.flags = shdr.sh_flags,
.type = shdr.sh_type,
});
const chunk = for (self.section_chunks.items) |*chunk| {
if (chunk.output_section_index == osec) break chunk;
} else blk: {
const chunk = try self.section_chunks.addOne(elf_file.base.comp.gpa);
chunk.* = .{ .output_section_index = osec };
break :blk chunk;
};
try chunk.atoms.append(elf_file.base.comp.gpa, atom_index);
}
}
pub fn allocateAtoms(self: *Object, elf_file: *Elf) !void {
for (self.section_chunks.items) |*chunk| {
chunk.updateSize(self);
}
for (self.section_chunks.items) |*chunk| {
const alloc_res = try elf_file.allocateChunk(.{
.shndx = chunk.output_section_index,
.size = chunk.size,
.alignment = chunk.alignment,
.requires_padding = false,
});
chunk.value = @intCast(alloc_res.value);
const slice = elf_file.sections.slice();
const shdr = &slice.items(.shdr)[chunk.output_section_index];
const last_atom_ref = &slice.items(.last_atom)[chunk.output_section_index];
const expand_section = if (elf_file.atom(alloc_res.placement)) |placement_atom|
placement_atom.nextAtom(elf_file) == null
else
true;
if (expand_section) last_atom_ref.* = chunk.lastAtom(self).ref();
shdr.sh_addralign = @max(shdr.sh_addralign, chunk.alignment.toByteUnits().?);
{
var idx: usize = 0;
while (idx < chunk.atoms.items.len) : (idx += 1) {
const curr_atom_ptr = self.atom(chunk.atoms.items[idx]).?;
if (idx > 0) {
curr_atom_ptr.prev_atom_ref = .{ .index = chunk.atoms.items[idx - 1], .file = self.index };
}
if (idx + 1 < chunk.atoms.items.len) {
curr_atom_ptr.next_atom_ref = .{ .index = chunk.atoms.items[idx + 1], .file = self.index };
}
}
}
if (elf_file.atom(alloc_res.placement)) |placement_atom| {
chunk.firstAtom(self).prev_atom_ref = placement_atom.ref();
chunk.lastAtom(self).next_atom_ref = placement_atom.next_atom_ref;
placement_atom.next_atom_ref = chunk.firstAtom(self).ref();
}
// TODO if we had a link from Atom to parent Chunk we would not need to update Atom's value or osec index
for (chunk.atoms.items) |atom_index| {
const atom_ptr = self.atom(atom_index).?;
atom_ptr.output_section_index = chunk.output_section_index;
atom_ptr.value += chunk.value;
}
}
}
pub fn writeAtoms(self: *Object, elf_file: *Elf) !void {
const gpa = elf_file.base.comp.gpa;
var undefs = std.AutoArrayHashMap(Elf.SymbolResolver.Index, std.ArrayList(Elf.Ref)).init(gpa);
defer {
for (undefs.values()) |*refs| {
refs.deinit();
}
undefs.deinit();
}
var buffer = std.ArrayList(u8).init(gpa);
defer buffer.deinit();
log.debug("writing atoms in {}", .{self.fmtPath()});
var has_reloc_errors = false;
for (self.section_chunks.items) |chunk| {
const osec = elf_file.sections.items(.shdr)[chunk.output_section_index];
if (osec.sh_type == elf.SHT_NOBITS) continue;
log.debug(" in section '{s}'", .{elf_file.getShString(osec.sh_name)});
try buffer.ensureUnusedCapacity(chunk.size);
buffer.appendNTimesAssumeCapacity(0, chunk.size);
for (chunk.atoms.items) |atom_index| {
const atom_ptr = self.atom(atom_index).?;
assert(atom_ptr.alive);
const offset = math.cast(usize, atom_ptr.value - chunk.value) orelse return error.Overflow;
const size = math.cast(usize, atom_ptr.size) orelse return error.Overflow;
log.debug(" * atom({d}) at 0x{x}", .{ atom_index, chunk.offset(elf_file) + offset });
const code = try self.codeDecompressAlloc(elf_file, atom_index);
defer gpa.free(code);
const out_code = buffer.items[offset..][0..size];
@memcpy(out_code, code);
const res = if (osec.sh_flags & elf.SHF_ALLOC == 0)
atom_ptr.resolveRelocsNonAlloc(elf_file, out_code, &undefs)
else
atom_ptr.resolveRelocsAlloc(elf_file, out_code);
_ = res catch |err| switch (err) {
error.UnsupportedCpuArch => {
try elf_file.reportUnsupportedCpuArch();
return error.FlushFailure;
},
error.RelocFailure, error.RelaxFailure => has_reloc_errors = true,
else => |e| return e,
};
}
try elf_file.base.file.?.pwriteAll(buffer.items, chunk.offset(elf_file));
buffer.clearRetainingCapacity();
}
try elf_file.reportUndefinedSymbols(&undefs);
if (has_reloc_errors) return error.FlushFailure;
}
pub fn writeAtomsRelocatable(self: *Object, elf_file: *Elf) !void {
const gpa = elf_file.base.comp.gpa;
var buffer = std.ArrayList(u8).init(gpa);
defer buffer.deinit();
log.debug("writing atoms in {}", .{self.fmtPath()});
for (self.section_chunks.items) |chunk| {
const osec = elf_file.sections.items(.shdr)[chunk.output_section_index];
if (osec.sh_type == elf.SHT_NOBITS) continue;
log.debug(" in section '{s}'", .{elf_file.getShString(osec.sh_name)});
try buffer.ensureUnusedCapacity(chunk.size);
buffer.appendNTimesAssumeCapacity(0, chunk.size);
for (chunk.atoms.items) |atom_index| {
const atom_ptr = self.atom(atom_index).?;
assert(atom_ptr.alive);
const offset = math.cast(usize, atom_ptr.value - chunk.value) orelse return error.Overflow;
const size = math.cast(usize, atom_ptr.size) orelse return error.Overflow;
log.debug(" * atom({d}) at 0x{x}", .{ atom_index, chunk.offset(elf_file) + offset });
const code = try self.codeDecompressAlloc(elf_file, atom_index);
defer gpa.free(code);
const out_code = buffer.items[offset..][0..size];
@memcpy(out_code, code);
}
try elf_file.base.file.?.pwriteAll(buffer.items, chunk.offset(elf_file));
buffer.clearRetainingCapacity();
const atom_list = &elf_file.sections.items(.atom_list_2)[osec];
atom_list.output_section_index = osec;
try atom_list.atoms.append(elf_file.base.comp.gpa, atom_ptr.ref());
}
}
@@ -1585,29 +1423,6 @@ fn formatAtoms(
}
}
pub fn fmtSectionChunks(self: *Object, elf_file: *Elf) std.fmt.Formatter(formatSectionChunks) {
return .{ .data = .{
.object = self,
.elf_file = elf_file,
} };
}
fn formatSectionChunks(
ctx: FormatContext,
comptime unused_fmt_string: []const u8,
options: std.fmt.FormatOptions,
writer: anytype,
) !void {
_ = unused_fmt_string;
_ = options;
const object = ctx.object;
const elf_file = ctx.elf_file;
try writer.writeAll(" section chunks\n");
for (object.section_chunks.items) |chunk| {
try writer.print(" {}\n", .{chunk.fmt(elf_file)});
}
}
pub fn fmtCies(self: *Object, elf_file: *Elf) std.fmt.Formatter(formatCies) {
return .{ .data = .{
.object = self,
@@ -1709,90 +1524,6 @@ const InArchive = struct {
size: u32,
};
const SectionChunk = struct {
value: i64 = 0,
size: u64 = 0,
alignment: Atom.Alignment = .@"1",
output_section_index: u32 = 0,
atoms: std.ArrayListUnmanaged(Atom.Index) = .{},
fn deinit(chunk: *SectionChunk, allocator: Allocator) void {
chunk.atoms.deinit(allocator);
}
fn address(chunk: SectionChunk, elf_file: *Elf) i64 {
const shdr = elf_file.sections.items(.shdr)[chunk.output_section_index];
return @as(i64, @intCast(shdr.sh_addr)) + chunk.value;
}
fn offset(chunk: SectionChunk, elf_file: *Elf) u64 {
const shdr = elf_file.sections.items(.shdr)[chunk.output_section_index];
return shdr.sh_offset + @as(u64, @intCast(chunk.value));
}
fn updateSize(chunk: *SectionChunk, object: *Object) void {
for (chunk.atoms.items) |atom_index| {
const atom_ptr = object.atom(atom_index).?;
assert(atom_ptr.alive);
const off = atom_ptr.alignment.forward(chunk.size);
const padding = off - chunk.size;
atom_ptr.value = @intCast(off);
chunk.size += padding + atom_ptr.size;
chunk.alignment = chunk.alignment.max(atom_ptr.alignment);
}
}
fn firstAtom(chunk: SectionChunk, object: *Object) *Atom {
assert(chunk.atoms.items.len > 0);
return object.atom(chunk.atoms.items[0]).?;
}
fn lastAtom(chunk: SectionChunk, object: *Object) *Atom {
assert(chunk.atoms.items.len > 0);
return object.atom(chunk.atoms.items[chunk.atoms.items.len - 1]).?;
}
pub fn format(
chunk: SectionChunk,
comptime unused_fmt_string: []const u8,
options: std.fmt.FormatOptions,
writer: anytype,
) !void {
_ = chunk;
_ = unused_fmt_string;
_ = options;
_ = writer;
@compileError("do not format SectionChunk directly");
}
const FormatCtx = struct { SectionChunk, *Elf };
pub fn fmt(chunk: SectionChunk, elf_file: *Elf) std.fmt.Formatter(format2) {
return .{ .data = .{ chunk, elf_file } };
}
fn format2(
ctx: FormatCtx,
comptime unused_fmt_string: []const u8,
options: std.fmt.FormatOptions,
writer: anytype,
) !void {
_ = unused_fmt_string;
_ = options;
const chunk, const elf_file = ctx;
try writer.print("chunk : @{x} : shdr({d}) : align({x}) : size({x})", .{
chunk.address(elf_file), chunk.output_section_index,
chunk.alignment.toByteUnits() orelse 0, chunk.size,
});
try writer.writeAll(" : atoms{ ");
for (chunk.atoms.items, 0..) |atom_index, i| {
try writer.print("{d}", .{atom_index});
if (i < chunk.atoms.items.len - 1) try writer.writeAll(", ");
}
try writer.writeAll(" }");
}
};
const Object = @This();
const std = @import("std");
@@ -1807,6 +1538,7 @@ const mem = std.mem;
const Allocator = mem.Allocator;
const Archive = @import("Archive.zig");
const Atom = @import("Atom.zig");
const AtomList = @import("AtomList.zig");
const Cie = eh_frame.Cie;
const Elf = @import("../Elf.zig");
const Fde = eh_frame.Fde;

View File

@@ -353,11 +353,13 @@ fn initComdatGroups(elf_file: *Elf) !void {
}
fn updateSectionSizes(elf_file: *Elf) !void {
for (elf_file.objects.items) |index| {
try elf_file.file(index).?.object.allocateAtoms(elf_file);
const slice = elf_file.sections.slice();
for (slice.items(.atom_list_2)) |*atom_list| {
if (atom_list.atoms.items.len == 0) continue;
atom_list.updateSize(elf_file);
try atom_list.allocate(elf_file);
}
const slice = elf_file.sections.slice();
for (slice.items(.shdr), 0..) |*shdr, shndx| {
const atom_list = slice.items(.atom_list)[shndx];
if (shdr.sh_type != elf.SHT_RELA) continue;
@@ -444,8 +446,16 @@ fn allocateAllocSections(elf_file: *Elf) !void {
}
fn writeAtoms(elf_file: *Elf) !void {
for (elf_file.objects.items) |index| {
try elf_file.file(index).?.object.writeAtomsRelocatable(elf_file);
const gpa = elf_file.base.comp.gpa;
var buffer = std.ArrayList(u8).init(gpa);
defer buffer.deinit();
const slice = elf_file.sections.slice();
for (slice.items(.shdr), slice.items(.atom_list_2)) |shdr, atom_list| {
if (shdr.sh_type == elf.SHT_NOBITS) continue;
if (atom_list.atoms.items.len == 0) continue;
try atom_list.writeRelocatable(&buffer, elf_file);
}
}