authorgravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2026-05-20 10:37:45+01:00
committergravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2026-05-20 10:40:11+01:00
logfec502ec674e458a53e846abb875e6591c3eacfb
tree2bcdb635316c0e1c5fb1c8a86dbddb442583a416
parent3dfcba86b856cfcf2aaef9e4ca636c7b2d2f9276
signaturelock-open Commit is signed but in an unrecognized format.

Elf2: flush ehdr phoff when rodata moves

I should have realised what was going on here sooner, because it was really simple! We had a file offset which was being flushed in `flushMoved` instead of `flushFileOffset`, and since `flushMoved` does not bubble down to the PHDR segment from the "parent" read-only LOAD segment, we weren't updating `ehdr.phoff` if the rodata segment had to move. The tricky thing which meant I didn't catch this sooner is that this wasn't happening on all filesystems, because the behavior of `link.MappedFile` differs depending on the capabilities of the target filesystem. Resolves: https://codeberg.org/ziglang/zig/issues/32123 Resolves: https://codeberg.org/ziglang/zig/issues/35367

1 files changed, 15 insertions(+), 8 deletions(-)

src/link/Elf2.zig+15-8
......@@ -4273,10 +4273,13 @@ fn flushFileOffset(elf: *Elf, ni: MappedFile.Node.Index) !void {
42734273 },
42744274 .segment => |phndx| {
42754275 switch (elf.phdrSlice()) {
4276 inline else => |phdr| elf.targetStore(
4277 &phdr[phndx].offset,
4278 @intCast(ni.fileLocation(&elf.mf, false).offset),
4279 ),
4276 inline else => |phdr, class| {
4277 const ph = &phdr[phndx];
4278 elf.targetStore(&ph.offset, @intCast(ni.fileLocation(&elf.mf, false).offset));
4279 if (elf.targetLoad(&ph.type) == .PHDR) {
4280 @field(elf.ehdrPtr(), @tagName(class)).phoff = ph.offset;
4281 }
4282 },
42804283 }
42814284 var child_it = ni.children(&elf.mf);
42824285 while (child_it.next()) |child_ni| try elf.flushFileOffset(child_ni);
......@@ -4296,14 +4299,18 @@ fn flushMoved(elf: *Elf, ni: MappedFile.Node.Index) !void {
42964299 .segment => |phndx| {
42974300 try elf.flushFileOffset(ni);
42984301 switch (elf.phdrSlice()) {
4299 inline else => |phdr, class| {
4302 inline else => |phdr| {
43004303 const ph = &phdr[phndx];
43014304 switch (elf.targetLoad(&ph.type)) {
43024305 else => unreachable,
43034306 .NULL, .LOAD => return,
4304 .DYNAMIC, .INTERP => {},
4305 .PHDR => @field(elf.ehdrPtr(), @tagName(class)).phoff = ph.offset,
4306 .TLS, std.elf.PT.GNU_RELRO => {},
4307
4308 .DYNAMIC,
4309 .INTERP,
4310 .PHDR,
4311 .TLS,
4312 .GNU_RELRO,
4313 => {},
43074314 }
43084315 elf.targetStore(&ph.vaddr, @intCast(elf.computeNodeVAddr(ni)));
43094316 ph.paddr = ph.vaddr;