diff --git a/src/Elf/Object.zig b/src/Elf/Object.zig index dc901727..b238a905 100644 --- a/src/Elf/Object.zig +++ b/src/Elf/Object.zig @@ -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 { diff --git a/src/Elf/eh_frame.zig b/src/Elf/eh_frame.zig index 0970dc60..ba9fc404 100644 --- a/src/Elf/eh_frame.zig +++ b/src/Elf/eh_frame.zig @@ -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(); @@ -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)); } } @@ -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)); } } } diff --git a/src/Elf/relocatable.zig b/src/Elf/relocatable.zig index 6027f1d4..705c3bd3 100644 --- a/src/Elf/relocatable.zig +++ b/src/Elf/relocatable.zig @@ -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; @@ -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}", .{ @@ -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); diff --git a/test/elf.zig b/test/elf.zig index 70955f5b..fba68844 100644 --- a/test/elf.zig +++ b/test/elf.zig @@ -2042,27 +2042,31 @@ fn testRelocatableEhFrame(b: *Build, opts: Options) *Step { ); obj2.addArg("-lc++"); + const obj3 = zig(b, "c.o", .obj); + obj3.addCppSource( + \\#include + \\#include + \\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 - \\#include - \\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(); @@ -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 - \\#include - \\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());