authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2024-10-29 21:17:11+01:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2024-10-29 17:27:42-07:00
log6ff267dc2635177b57bda1d398374ab96ed40179
tree7ba6df1b5ec1a308c6166b1990247e4015622857
parent4661705a0e9b28ea008ffb85258daf1d5510b054

link/Elf: ensure we always sort all relocations by r_offset in -r mode

According to a comment in mold, this is the expected (and desired) condition by the linkers, except for some architectures (RISCV and Loongarch) where this condition does not have to upheld. If you follow the changes in this patch and in particular doc comments I have linked the comment/code in mold that explains and implements this. I have also modified `testEhFrameRelocatable` test to now test both cases such that `zig ld -r a.o b.o -o c.o` and `zig ld -r b.o a.o -o d.o`. In both cases, `c.o` and `d.o` should produce valid object files which was not the case before this patch.

4 files changed, 72 insertions(+), 45 deletions(-)

src/link/Elf/Object.zig+4
......@@ -398,6 +398,10 @@ fn parseEhFrame(
398398 defer gpa.free(relocs);
399399 const rel_start: u32 = @intCast(self.relocs.items.len);
400400 try self.relocs.appendUnalignedSlice(gpa, relocs);
401
402 // We expect relocations to be sorted by r_offset as per this comment in mold linker:
403 // https://github.com/rui314/mold/blob/8e4f7b53832d8af4f48a633a8385cbc932d1944e/src/input-files.cc#L653
404 // Except for RISCV and Loongarch which do not seem to be uphold this convention.
401405 if (target.cpu.arch == .riscv64) {
402406 sortRelocs(self.relocs.items[rel_start..][0..relocs.len]);
403407 }
src/link/Elf/eh_frame.zig+4-7
......@@ -453,7 +453,7 @@ fn emitReloc(elf_file: *Elf, r_offset: u64, sym: *const Symbol, rel: elf.Elf64_R
453453 };
454454}
455455
456pub fn writeEhFrameRelocs(elf_file: *Elf, writer: anytype) !void {
456pub fn writeEhFrameRelocs(elf_file: *Elf, relocs: *std.ArrayList(elf.Elf64_Rela)) !void {
457457 relocs_log.debug("{x}: .eh_frame", .{
458458 elf_file.sections.items(.shdr)[elf_file.section_indexes.eh_frame.?].sh_addr,
459459 });
......@@ -466,8 +466,7 @@ pub fn writeEhFrameRelocs(elf_file: *Elf, writer: anytype) !void {
466466 for (atom_ptr.relocs(elf_file)) |rel| {
467467 const ref = zo.resolveSymbol(rel.r_sym(), elf_file);
468468 const target = elf_file.symbol(ref).?;
469 const out_rel = emitReloc(elf_file, rel.r_offset, target, rel);
470 try writer.writeStruct(out_rel);
469 try relocs.append(emitReloc(elf_file, rel.r_offset, target, rel));
471470 }
472471 }
473472
......@@ -480,8 +479,7 @@ pub fn writeEhFrameRelocs(elf_file: *Elf, writer: anytype) !void {
480479 const ref = object.resolveSymbol(rel.r_sym(), elf_file);
481480 const sym = elf_file.symbol(ref).?;
482481 const r_offset = cie.address(elf_file) + rel.r_offset - cie.offset;
483 const out_rel = emitReloc(elf_file, r_offset, sym, rel);
484 try writer.writeStruct(out_rel);
482 try relocs.append(emitReloc(elf_file, r_offset, sym, rel));
485483 }
486484 }
487485
......@@ -491,8 +489,7 @@ pub fn writeEhFrameRelocs(elf_file: *Elf, writer: anytype) !void {
491489 const ref = object.resolveSymbol(rel.r_sym(), elf_file);
492490 const sym = elf_file.symbol(ref).?;
493491 const r_offset = fde.address(elf_file) + rel.r_offset - fde.offset;
494 const out_rel = emitReloc(elf_file, r_offset, sym, rel);
495 try writer.writeStruct(out_rel);
492 try relocs.append(emitReloc(elf_file, r_offset, sym, rel));
496493 }
497494 }
498495 }
src/link/Elf/relocatable.zig+21-15
......@@ -362,6 +362,14 @@ fn writeSyntheticSections(elf_file: *Elf) !void {
362362 const gpa = elf_file.base.comp.gpa;
363363 const slice = elf_file.sections.slice();
364364
365 const SortRelocs = struct {
366 pub fn lessThan(ctx: void, lhs: elf.Elf64_Rela, rhs: elf.Elf64_Rela) bool {
367 _ = ctx;
368 assert(lhs.r_offset != rhs.r_offset);
369 return lhs.r_offset < rhs.r_offset;
370 }
371 };
372
365373 for (slice.items(.shdr), slice.items(.atom_list), 0..) |shdr, atom_list, shndx| {
366374 if (shdr.sh_type != elf.SHT_RELA) continue;
367375 if (atom_list.items.len == 0) continue;
......@@ -378,15 +386,8 @@ fn writeSyntheticSections(elf_file: *Elf) !void {
378386 try atom_ptr.writeRelocs(elf_file, &relocs);
379387 }
380388 assert(relocs.items.len == num_relocs);
381
382 const SortRelocs = struct {
383 pub fn lessThan(ctx: void, lhs: elf.Elf64_Rela, rhs: elf.Elf64_Rela) bool {
384 _ = ctx;
385 assert(lhs.r_offset != rhs.r_offset);
386 return lhs.r_offset < rhs.r_offset;
387 }
388 };
389
389 // Sort output relocations by r_offset which is usually an expected (and desired) condition
390 // by the linkers.
390391 mem.sortUnstable(elf.Elf64_Rela, relocs.items, {}, SortRelocs.lessThan);
391392
392393 log.debug("writing {s} from 0x{x} to 0x{x}", .{
......@@ -418,16 +419,21 @@ fn writeSyntheticSections(elf_file: *Elf) !void {
418419 }
419420 if (elf_file.section_indexes.eh_frame_rela) |shndx| {
420421 const shdr = slice.items(.shdr)[shndx];
421 const sh_size = math.cast(usize, shdr.sh_size) orelse return error.Overflow;
422 var buffer = try std.ArrayList(u8).initCapacity(gpa, sh_size);
423 defer buffer.deinit();
424 try eh_frame.writeEhFrameRelocs(elf_file, buffer.writer());
425 assert(buffer.items.len == sh_size);
422 const num_relocs = math.cast(usize, @divExact(shdr.sh_size, shdr.sh_entsize)) orelse
423 return error.Overflow;
424 var relocs = try std.ArrayList(elf.Elf64_Rela).initCapacity(gpa, num_relocs);
425 defer relocs.deinit();
426 try eh_frame.writeEhFrameRelocs(elf_file, &relocs);
427 assert(relocs.items.len == num_relocs);
428 // Sort output relocations by r_offset which is usually an expected (and desired) condition
429 // by the linkers.
430 mem.sortUnstable(elf.Elf64_Rela, relocs.items, {}, SortRelocs.lessThan);
431
426432 log.debug("writing .rela.eh_frame from 0x{x} to 0x{x}", .{
427433 shdr.sh_offset,
428434 shdr.sh_offset + shdr.sh_size,
429435 });
430 try elf_file.base.file.?.pwriteAll(buffer.items, shdr.sh_offset);
436 try elf_file.base.file.?.pwriteAll(mem.sliceAsBytes(relocs.items), shdr.sh_offset);
431437 }
432438
433439 try writeComdatGroups(elf_file);
test/link/elf.zig+43-23
......@@ -2744,32 +2744,52 @@ fn testRelocatableEhFrame(b: *Build, opts: Options) *Step {
27442744 ,
27452745 });
27462746 obj2.linkLibCpp();
2747 const obj3 = addObject(b, opts, .{ .name = "obj3", .cpp_source_bytes =
2748 \\#include <iostream>
2749 \\#include <stdexcept>
2750 \\extern int try_again();
2751 \\int main() {
2752 \\ try {
2753 \\ try_again();
2754 \\ } catch (const std::exception &e) {
2755 \\ std::cout << "exception=" << e.what();
2756 \\ }
2757 \\ return 0;
2758 \\}
2759 });
2760 obj3.linkLibCpp();
27472761
2748 const obj = addObject(b, opts, .{ .name = "obj" });
2749 obj.addObject(obj1);
2750 obj.addObject(obj2);
2751 obj.linkLibCpp();
2762 {
2763 const obj = addObject(b, opts, .{ .name = "obj" });
2764 obj.addObject(obj1);
2765 obj.addObject(obj2);
2766 obj.linkLibCpp();
27522767
2753 const exe = addExecutable(b, opts, .{ .name = "test1" });
2754 addCppSourceBytes(exe,
2755 \\#include <iostream>
2756 \\#include <stdexcept>
2757 \\extern int try_again();
2758 \\int main() {
2759 \\ try {
2760 \\ try_again();
2761 \\ } catch (const std::exception &e) {
2762 \\ std::cout << "exception=" << e.what();
2763 \\ }
2764 \\ return 0;
2765 \\}
2766 , &.{});
2767 exe.addObject(obj);
2768 exe.linkLibCpp();
2768 const exe = addExecutable(b, opts, .{ .name = "test1" });
2769 exe.addObject(obj3);
2770 exe.addObject(obj);
2771 exe.linkLibCpp();
27692772
2770 const run = addRunArtifact(exe);
2771 run.expectStdOutEqual("exception=Oh no!");
2772 test_step.dependOn(&run.step);
2773 const run = addRunArtifact(exe);
2774 run.expectStdOutEqual("exception=Oh no!");
2775 test_step.dependOn(&run.step);
2776 }
2777 {
2778 // Flipping the order should not influence the end result.
2779 const obj = addObject(b, opts, .{ .name = "obj" });
2780 obj.addObject(obj2);
2781 obj.addObject(obj1);
2782 obj.linkLibCpp();
2783
2784 const exe = addExecutable(b, opts, .{ .name = "test2" });
2785 exe.addObject(obj3);
2786 exe.addObject(obj);
2787 exe.linkLibCpp();
2788
2789 const run = addRunArtifact(exe);
2790 run.expectStdOutEqual("exception=Oh no!");
2791 test_step.dependOn(&run.step);
2792 }
27732793
27742794 return test_step;
27752795}