Skip to content

Commit

Permalink
Merge pull request #165 from kubkon/elf-relocatable-eh-frame-fixes
Browse files Browse the repository at this point in the history
elf: ensure we sort .eh_frame relocs in -r mode
  • Loading branch information
kubkon authored Oct 29, 2024
2 parents 9ba65d2 + 0cd8dba commit 8d85ffd
Show file tree
Hide file tree
Showing 4 changed files with 60 additions and 51 deletions.
5 changes: 5 additions & 0 deletions src/Elf/Object.zig
Original file line number Diff line number Diff line change
Expand Up @@ -406,6 +406,11 @@ fn parseEhFrame(self: *Object, allocator: Allocator, file: std.fs.File, shndx: u
}
}

/// We expect relocations sorted by r_offset in relocation tables as per this
/// comment in mold linker
/// https://github.com/rui314/mold/blob/8e4f7b53832d8af4f48a633a8385cbc932d1944e/src/input-files.cc#L653.
/// There are exceptions to this however, namely, RISCV and Loongarch
/// do not follow this convention.
fn sortRelocs(relocs: []elf.Elf64_Rela, ctx: *Elf) void {
const sortFn = struct {
fn lessThan(c: void, lhs: elf.Elf64_Rela, rhs: elf.Elf64_Rela) bool {
Expand Down
8 changes: 3 additions & 5 deletions src/Elf/eh_frame.zig
Original file line number Diff line number Diff line change
Expand Up @@ -476,7 +476,7 @@ fn emitReloc(elf_file: *Elf, rec: anytype, sym: Symbol, rel: elf.Elf64_Rela) elf
};
}

pub fn writeEhFrameRelocs(elf_file: *Elf, writer: anytype) !void {
pub fn writeEhFrameRelocs(elf_file: *Elf, relocs: *std.ArrayList(elf.Elf64_Rela)) !void {
const tracy = trace(@src());
defer tracy.end();

Expand All @@ -490,8 +490,7 @@ pub fn writeEhFrameRelocs(elf_file: *Elf, writer: anytype) !void {
for (cie.getRelocs(elf_file)) |rel| {
const sym_ref = object.resolveSymbol(rel.r_sym(), elf_file);
const sym = elf_file.getSymbol(sym_ref).?;
const out_rel = emitReloc(elf_file, cie, sym.*, rel);
try writer.writeStruct(out_rel);
try relocs.append(emitReloc(elf_file, cie, sym.*, rel));
}
}

Expand All @@ -500,8 +499,7 @@ pub fn writeEhFrameRelocs(elf_file: *Elf, writer: anytype) !void {
for (fde.getRelocs(elf_file)) |rel| {
const sym_ref = object.resolveSymbol(rel.r_sym(), elf_file);
const sym = elf_file.getSymbol(sym_ref).?;
const out_rel = emitReloc(elf_file, fde, sym.*, rel);
try writer.writeStruct(out_rel);
try relocs.append(emitReloc(elf_file, fde, sym.*, rel));
}
}
}
Expand Down
27 changes: 14 additions & 13 deletions src/Elf/relocatable.zig
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,13 @@ fn writeSyntheticSections(elf_file: *Elf) !void {

const gpa = elf_file.base.allocator;

const SortRelocs = struct {
pub fn lessThan(ctx: void, lhs: elf.Elf64_Rela, rhs: elf.Elf64_Rela) bool {
_ = ctx;
return lhs.r_offset < rhs.r_offset;
}
};

for (elf_file.sections.items(.rela_shndx), elf_file.sections.items(.atoms)) |rela_shndx, atoms| {
if (atoms.items.len == 0) continue;

Expand All @@ -233,14 +240,6 @@ fn writeSyntheticSections(elf_file: *Elf) !void {
try atom.writeRelocs(elf_file, &relocs);
}
assert(relocs.items.len == num_relocs);

const SortRelocs = struct {
pub fn lessThan(ctx: void, lhs: elf.Elf64_Rela, rhs: elf.Elf64_Rela) bool {
_ = ctx;
return lhs.r_offset < rhs.r_offset;
}
};

mem.sort(elf.Elf64_Rela, relocs.items, {}, SortRelocs.lessThan);

log.debug("writing {s} from 0x{x} to 0x{x}", .{
Expand Down Expand Up @@ -268,17 +267,19 @@ fn writeSyntheticSections(elf_file: *Elf) !void {

const rela_shndx = elf_file.sections.items(.rela_shndx)[shndx];
const rela_shdr = elf_file.sections.items(.shdr)[rela_shndx];
buffer.clearRetainingCapacity();
try buffer.ensureTotalCapacityPrecise(rela_shdr.sh_size);
try eh_frame.writeEhFrameRelocs(elf_file, buffer.writer());
const num_relocs = @divExact(rela_shdr.sh_size, rela_shdr.sh_entsize);
var relocs = try std.ArrayList(elf.Elf64_Rela).initCapacity(gpa, num_relocs);
defer relocs.deinit();
try eh_frame.writeEhFrameRelocs(elf_file, &relocs);
assert(relocs.items.len == num_relocs);
mem.sort(elf.Elf64_Rela, relocs.items, {}, SortRelocs.lessThan);

log.debug("writing .rela.eh_frame from 0x{x} to 0x{x}", .{
rela_shdr.sh_offset,
rela_shdr.sh_offset + rela_shdr.sh_size,
});

assert(buffer.items.len == rela_shdr.sh_size);
try elf_file.base.file.pwriteAll(buffer.items, rela_shdr.sh_offset);
try elf_file.base.file.pwriteAll(mem.sliceAsBytes(relocs.items), rela_shdr.sh_offset);
}

try writeComdatGroup(elf_file);
Expand Down
71 changes: 38 additions & 33 deletions test/elf.zig
Original file line number Diff line number Diff line change
Expand Up @@ -2042,27 +2042,31 @@ fn testRelocatableEhFrame(b: *Build, opts: Options) *Step {
);
obj2.addArg("-lc++");

const obj3 = zig(b, "c.o", .obj);
obj3.addCppSource(
\\#include <iostream>
\\#include <stdexcept>
\\extern int try_again();
\\int main() {
\\ try {
\\ try_again();
\\ } catch (const std::exception &e) {
\\ std::cout << "exception=" << e.what();
\\ }
\\ return 0;
\\}
);
obj3.addArg("-lc++");

{
const obj3 = ld(b, "c.o", opts);
obj3.addFileSource(obj1.getFile());
obj3.addFileSource(obj2.getFile());
obj3.addArg("-r");
const obj4 = ld(b, "d.o", opts);
obj4.addFileSource(obj1.getFile());
obj4.addFileSource(obj2.getFile());
obj4.addArg("-r");

const exe = zig(b, "a.out", .exe);
exe.addCppSource(
\\#include <iostream>
\\#include <stdexcept>
\\extern int try_again();
\\int main() {
\\ try {
\\ try_again();
\\ } catch (const std::exception &e) {
\\ std::cout << "exception=" << e.what();
\\ }
\\ return 0;
\\}
);
exe.addFileSource(obj3.getFile());
exe.addFileSource(obj4.getFile());
exe.addArg("-lc++");

const run = exe.run();
Expand All @@ -2071,23 +2075,24 @@ fn testRelocatableEhFrame(b: *Build, opts: Options) *Step {
}

{
// Let's make the object file COMDAT group heavy!
const obj3 = zig(b, "c.o", .obj);
obj3.addCppSource(
\\#include <iostream>
\\#include <stdexcept>
\\extern int try_again();
\\int main() {
\\ try {
\\ try_again();
\\ } catch (const std::exception &e) {
\\ std::cout << "exception=" << e.what();
\\ }
\\ return 0;
\\}
);
obj3.addArg("-lc++");
// Flipping the order should not matter.
const obj4 = ld(b, "d.o", opts);
obj4.addFileSource(obj2.getFile());
obj4.addFileSource(obj1.getFile());
obj4.addArg("-r");

const exe = zig(b, "a.out", .exe);
exe.addFileSource(obj3.getFile());
exe.addFileSource(obj4.getFile());
exe.addArg("-lc++");

const run = exe.run();
run.expectStdOutEqual("exception=Oh no!");
test_step.dependOn(run.step());
}

{
// Let's make the object file COMDAT group heavy!
const obj4 = ld(b, "d.o", opts);
obj4.addFileSource(obj1.getFile());
obj4.addFileSource(obj2.getFile());
Expand Down

0 comments on commit 8d85ffd

Please sign in to comment.