Skip to content

Commit

Permalink
elf: fix spawning merge sections
Browse files Browse the repository at this point in the history
  • Loading branch information
kubkon committed Sep 20, 2024
1 parent 9cdcf61 commit e721943
Show file tree
Hide file tree
Showing 6 changed files with 184 additions and 184 deletions.
146 changes: 75 additions & 71 deletions src/Elf.zig
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,6 @@ comdat_group_sections: std.ArrayListUnmanaged(ComdatGroupSection) = .{},
thunks: std.ArrayListUnmanaged(Thunk) = .{},

merge_sections: std.ArrayListUnmanaged(MergeSection) = .{},
merge_subsections: std.ArrayListUnmanaged(MergeSubsection) = .{},

has_text_reloc: bool = false,
num_ifunc_dynrelocs: usize = 0,
Expand Down Expand Up @@ -121,7 +120,6 @@ pub fn deinit(self: *Elf) void {
sect.deinit(gpa);
}
self.merge_sections.deinit(gpa);
self.merge_subsections.deinit(gpa);
self.resolver.deinit(gpa);
self.got.deinit(gpa);
self.plt.deinit(gpa);
Expand Down Expand Up @@ -364,7 +362,6 @@ pub fn flush(self: *Elf) !void {

try self.initSyntheticSections();
try self.sortSections();
try self.addAtomsToSections();
try self.sortInitFini();
try self.setDynamic();
self.setDynsym();
Expand Down Expand Up @@ -473,32 +470,12 @@ fn sortInitFini(self: *Elf) !void {

fn initOutputSections(self: *Elf) !void {
for (self.objects.items) |index| {
const object = self.getFile(index).?.object;
for (object.atoms_indexes.items) |atom_index| {
const atom = object.getAtom(atom_index) orelse continue;
if (!atom.flags.alive) continue;
atom.out_shndx = try object.initOutputSection(self, atom.getInputShdr(self));
}
try self.getFile(index).?.object.initOutputSections(self);
}

for (self.merge_sections.items) |*msec| {
if (msec.subsections.items.len == 0) continue;
const shndx = self.getSectionByName(msec.getName(self)) orelse try self.addSection(.{
.name = msec.name,
.type = msec.type,
.flags = msec.flags,
});
msec.out_shndx = shndx;

var entsize = self.getMergeSubsection(msec.subsections.items[0]).entsize;
for (msec.subsections.items) |index| {
const msub = self.getMergeSubsection(index);
entsize = @min(entsize, msub.entsize);
}
const shdr = &self.sections.items(.shdr)[shndx];
shdr.sh_entsize = entsize;
if (msec.finalized_subsections.items.len == 0) continue;
try msec.initOutputSection(self);
}

self.text_sect_index = self.getSectionByName(".text");
}

Expand Down Expand Up @@ -697,28 +674,13 @@ pub fn initShStrtab(self: *Elf) !void {
});
}

pub fn addAtomsToSections(self: *Elf) !void {
for (self.objects.items) |index| {
const object = self.getFile(index).?.object;
for (object.atoms_indexes.items) |atom_index| {
const atom = object.getAtom(atom_index) orelse continue;
if (!atom.flags.alive) continue;
const atoms = &self.sections.items(.atoms)[atom.out_shndx];
try atoms.append(self.base.allocator, .{
.index = atom_index,
.file = index,
});
}
}
}

pub fn addCommentString(self: *Elf) !void {
const msec_index = try self.getOrCreateMergeSection(".comment", elf.SHF_MERGE | elf.SHF_STRINGS, elf.SHT_PROGBITS);
const msec = self.getMergeSection(msec_index);
const res = try msec.insertZ(self.base.allocator, Options.version);
if (res.found_existing) return;
const msub_index = try self.addMergeSubsection();
const msub = self.getMergeSubsection(msub_index);
const msub_index = try msec.addMergeSubsection(self.base.allocator);
const msub = msec.getMergeSubsection(msub_index);
msub.merge_section = msec_index;
msub.string_index = res.key.pos;
msub.alignment = 0;
Expand All @@ -730,23 +692,23 @@ pub fn addCommentString(self: *Elf) !void {

pub fn finalizeMergeSections(self: *Elf) !void {
for (self.merge_sections.items) |*msec| {
try msec.finalize(self);
try msec.finalize(self.base.allocator);
}
}

pub fn calcMergeSectionSizes(self: *Elf) !void {
for (self.merge_sections.items) |*msec| {
try msec.calcSize();
}
for (self.merge_sections.items) |*msec| {
const shdr = &self.sections.items(.shdr)[msec.out_shndx];
for (msec.subsections.items) |msub_index| {
const msub = self.getMergeSubsection(msub_index);
assert(msub.alive);
const alignment = try math.powi(u64, 2, msub.alignment);
const offset = mem.alignForward(u64, shdr.sh_size, alignment);
const padding = offset - shdr.sh_size;
msub.value = @intCast(offset);
shdr.sh_size += padding + msub.size;
shdr.sh_addralign = @max(shdr.sh_addralign, alignment);
}
const alignment = try math.powi(u64, 2, msec.alignment);
const offset = mem.alignForward(u64, shdr.sh_size, alignment);
const padding = offset - shdr.sh_size;
msec.value = @intCast(offset);
shdr.sh_size += padding + msec.size;
shdr.sh_addralign = @max(shdr.sh_addralign, alignment);
shdr.sh_entsize = if (shdr.sh_entsize == 0) msec.entsize else @min(shdr.sh_entsize, msec.entsize);
}
}

Expand Down Expand Up @@ -2118,21 +2080,22 @@ pub fn writeMergeSections(self: *Elf) !void {
var buffer = std.ArrayList(u8).init(gpa);
defer buffer.deinit();

for (self.merge_sections.items) |msec| {
for (self.merge_sections.items) |*msec| {
const shdr = self.sections.items(.shdr)[msec.out_shndx];
const fileoff = msec.value + shdr.sh_offset;

try buffer.ensureTotalCapacity(shdr.sh_size);
buffer.appendNTimesAssumeCapacity(0, shdr.sh_size);
try buffer.ensureTotalCapacity(msec.size);
buffer.appendNTimesAssumeCapacity(0, msec.size);

for (msec.subsections.items) |msub_index| {
const msub = self.getMergeSubsection(msub_index);
for (msec.finalized_subsections.items) |msub_index| {
const msub = msec.getMergeSubsection(msub_index);
assert(msub.alive);
const string = msub.getString(self);
const string = msub.getString(msec);
const off: u64 = @intCast(msub.value);
@memcpy(buffer.items[off..][0..string.len], string);
}

try self.base.file.pwriteAll(buffer.items, shdr.sh_offset);
try self.base.file.pwriteAll(buffer.items, fileoff);
buffer.clearRetainingCapacity();
}
}
Expand Down Expand Up @@ -2496,16 +2459,57 @@ fn createThunks(self: *Elf, shndx: u32) !void {
}
}

pub fn addMergeSubsection(self: *Elf) !MergeSubsection.Index {
const index: MergeSubsection.Index = @intCast(self.merge_subsections.items.len);
const msec = try self.merge_subsections.addOne(self.base.allocator);
msec.* = .{};
return index;
}

pub fn getMergeSubsection(self: *Elf, index: MergeSubsection.Index) *MergeSubsection {
assert(index < self.merge_subsections.items.len);
return &self.merge_subsections.items[index];
pub fn initOutputSection(self: *Elf, args: struct {
name: [:0]const u8,
flags: u64,
type: u32,
}) error{OutOfMemory}!u32 {
const name = blk: {
if (self.options.relocatable) break :blk args.name;
if (args.flags & elf.SHF_MERGE != 0) break :blk args.name;
const name_prefixes: []const [:0]const u8 = &.{
".text", ".data.rel.ro", ".data", ".rodata", ".bss.rel.ro", ".bss",
".init_array", ".fini_array", ".tbss", ".tdata", ".gcc_except_table", ".ctors",
".dtors", ".gnu.warning",
};
inline for (name_prefixes) |prefix| {
if (std.mem.eql(u8, args.name, prefix) or std.mem.startsWith(u8, args.name, prefix ++ ".")) {
break :blk prefix;
}
}
break :blk args.name;
};
const @"type" = tt: {
if (self.options.cpu_arch.? == .x86_64 and args.type == elf.SHT_X86_64_UNWIND)
break :tt elf.SHT_PROGBITS;
switch (args.type) {
elf.SHT_NULL => unreachable,
elf.SHT_PROGBITS => {
if (std.mem.eql(u8, args.name, ".init_array") or std.mem.startsWith(u8, args.name, ".init_array."))
break :tt elf.SHT_INIT_ARRAY;
if (std.mem.eql(u8, args.name, ".fini_array") or std.mem.startsWith(u8, args.name, ".fini_array."))
break :tt elf.SHT_FINI_ARRAY;
break :tt args.type;
},
else => break :tt args.type,
}
};
const flags = blk: {
var flags = args.flags;
if (!self.options.relocatable) {
flags &= ~@as(u64, elf.SHF_COMPRESSED | elf.SHF_GROUP | elf.SHF_GNU_RETAIN);
}
break :blk switch (@"type") {
elf.SHT_INIT_ARRAY, elf.SHT_FINI_ARRAY => flags | elf.SHF_WRITE,
else => flags,
};
};
const out_shndx = self.getSectionByName(name) orelse try self.addSection(.{
.type = @"type",
.flags = flags,
.name = try self.insertShString(name),
});
return out_shndx;
}

pub fn getOrCreateMergeSection(self: *Elf, name: [:0]const u8, flags: u64, @"type": u32) !MergeSection.Index {
Expand Down
4 changes: 4 additions & 0 deletions src/Elf/Atom.zig
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,10 @@ pub fn getAddress(self: Atom, elf_file: *Elf) i64 {
return @as(i64, @intCast(shdr.sh_addr)) + self.value;
}

pub fn getRef(self: Atom) Elf.Ref {
return .{ .index = self.atom_index, .file = self.file };
}

pub fn getDebugTombstoneValue(self: Atom, target: Symbol, elf_file: *Elf) ?u64 {
if (target.getMergeSubsection(elf_file)) |msub| {
if (msub.alive) return null;
Expand Down
101 changes: 42 additions & 59 deletions src/Elf/Object.zig
Original file line number Diff line number Diff line change
Expand Up @@ -308,56 +308,6 @@ fn initSymbols(self: *Object, allocator: Allocator, elf_file: *Elf) !void {
}
}

pub fn initOutputSection(self: Object, elf_file: *Elf, shdr: elf.Elf64_Shdr) !u32 {
const name = blk: {
const name = self.getString(shdr.sh_name);
if (elf_file.options.relocatable) break :blk name;
if (shdr.sh_flags & elf.SHF_MERGE != 0) break :blk name;
const sh_name_prefixes: []const [:0]const u8 = &.{
".text", ".data.rel.ro", ".data", ".rodata", ".bss.rel.ro", ".bss",
".init_array", ".fini_array", ".tbss", ".tdata", ".gcc_except_table", ".ctors",
".dtors", ".gnu.warning",
};
inline for (sh_name_prefixes) |prefix| {
if (std.mem.eql(u8, name, prefix) or std.mem.startsWith(u8, name, prefix ++ ".")) {
break :blk prefix;
}
}
break :blk name;
};
const @"type" = tt: {
if (elf_file.options.cpu_arch.? == .x86_64 and shdr.sh_type == elf.SHT_X86_64_UNWIND) break :tt elf.SHT_PROGBITS;

const @"type" = switch (shdr.sh_type) {
elf.SHT_NULL => unreachable,
elf.SHT_PROGBITS => blk: {
if (std.mem.eql(u8, name, ".init_array") or std.mem.startsWith(u8, name, ".init_array."))
break :blk elf.SHT_INIT_ARRAY;
if (std.mem.eql(u8, name, ".fini_array") or std.mem.startsWith(u8, name, ".fini_array."))
break :blk elf.SHT_FINI_ARRAY;
break :blk shdr.sh_type;
},
else => shdr.sh_type,
};
break :tt @"type";
};
const flags = blk: {
var flags = shdr.sh_flags;
if (!elf_file.options.relocatable) {
flags &= ~@as(u64, elf.SHF_COMPRESSED | elf.SHF_GROUP | elf.SHF_GNU_RETAIN);
}
break :blk switch (@"type") {
elf.SHT_INIT_ARRAY, elf.SHT_FINI_ARRAY => flags | elf.SHF_WRITE,
else => flags,
};
};
return elf_file.getSectionByName(name) orelse try elf_file.addSection(.{
.type = @"type",
.flags = flags,
.name = try elf_file.insertShString(name),
});
}

fn parseEhFrame(self: *Object, allocator: Allocator, file: std.fs.File, shndx: u32, elf_file: *Elf) !void {
const tracy = trace(@src());
defer tracy.end();
Expand Down Expand Up @@ -754,6 +704,42 @@ pub fn initMergeSections(self: *Object, elf_file: *Elf) !void {
}
}

pub fn initOutputSections(self: *Object, elf_file: *Elf) !void {
for (self.atoms_indexes.items) |atom_index| {
const atom_ptr = self.getAtom(atom_index) orelse continue;
if (!atom_ptr.flags.alive) continue;
const shdr = atom_ptr.getInputShdr(elf_file);
const osec = try elf_file.initOutputSection(.{
.name = self.getString(shdr.sh_name),
.flags = shdr.sh_flags,
.type = shdr.sh_type,
});
atom_ptr.out_shndx = osec;
const atoms = &elf_file.sections.items(.atoms)[osec];
try atoms.append(elf_file.base.allocator, atom_ptr.getRef());
}
}

pub fn initRelaSections(self: *Object, elf_file: *Elf) !void {
for (self.atoms_indexes.items) |atom_index| {
const atom_ptr = self.getAtom(atom_index) orelse continue;
if (!atom_ptr.flags.alive) continue;
if (atom_ptr.getRelocs(elf_file).len == 0) continue;
const shdr = self.shdrs.items[atom_ptr.relocs_shndx];
const out_shndx = try elf_file.initOutputSection(.{
.name = self.getString(shdr.sh_name),
.flags = shdr.sh_flags,
.type = shdr.sh_type,
});
const out_shdr = &elf_file.sections.items(.shdr)[out_shndx];
out_shdr.sh_type = elf.SHT_RELA;
out_shdr.sh_addralign = @alignOf(elf.Elf64_Rela);
out_shdr.sh_entsize = @sizeOf(elf.Elf64_Rela);
out_shdr.sh_flags |= elf.SHF_INFO_LINK;
elf_file.sections.items(.rela_shndx)[atom_ptr.out_shndx] = out_shndx;
}
}

pub fn resolveMergeSubsections(self: *Object, elf_file: *Elf) !void {
const gpa = elf_file.base.allocator;

Expand All @@ -770,8 +756,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.getMergeSubsection(msub_index);
const msub_index = try msec.addMergeSubsection(gpa);
const msub = msec.getMergeSubsection(msub_index);
msub.merge_section = imsec.merge_section;
msub.string_index = res.key.pos;
msub.entsize = @intCast(isec.sh_entsize);
Expand Down Expand Up @@ -820,6 +806,7 @@ pub fn resolveMergeSubsections(self: *Object, elf_file: *Elf) !void {
const imsec_index = self.merge_sections_indexes.items[esym.st_shndx];
const imsec = self.getInputMergeSection(imsec_index) orelse continue;
if (imsec.offsets.items.len == 0) continue;
const msec = elf_file.getMergeSection(imsec.merge_section);
const msub_index, const offset = imsec.findSubsection(@intCast(@as(i64, @intCast(esym.st_value)) + rel.r_addend)) orelse {
elf_file.base.fatal("{}: {s}: invalid relocation at offset 0x{x}", .{
self.fmtPath(),
Expand All @@ -828,18 +815,14 @@ pub fn resolveMergeSubsections(self: *Object, elf_file: *Elf) !void {
});
return error.ParseFailed;
};
const msub = elf_file.getMergeSubsection(msub_index);
const msec = msub.getMergeSection(elf_file);

const out_sym_idx: u64 = @intCast(self.symbols.items.len);
try self.symbols.ensureUnusedCapacity(gpa, 1);
const sym_index = try self.addSymbol(gpa);
const sym = &self.symbols.items[sym_index];
const name = try std.fmt.allocPrint(gpa, "{s}$subsection{d}", .{
msec.getName(elf_file),
msub_index,
});
defer gpa.free(name);
const sym_index = try self.addSymbol(gpa);
const sym = &self.symbols.items[sym_index];
sym.* = .{
.value = @bitCast(@as(i64, @intCast(offset)) - rel.r_addend),
.name = try self.addString(gpa, name),
Expand All @@ -849,7 +832,7 @@ pub fn resolveMergeSubsections(self: *Object, elf_file: *Elf) !void {
};
sym.ref = .{ .index = msub_index, .file = imsec.merge_section };
sym.flags.merge_subsection = true;
rel.r_info = (out_sym_idx << 32) | rel.r_type();
rel.r_info = (@as(u64, @intCast(sym_index)) << 32) | rel.r_type();
}
}
}
Expand Down
Loading

0 comments on commit e721943

Please sign in to comment.