From fec502ec674e458a53e846abb875e6591c3eacfb Mon Sep 17 00:00:00 2001 From: Matthew Lugg Date: Wed, 20 May 2026 10:37:45 +0100 Subject: [PATCH] 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 --- src/link/Elf2.zig | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/src/link/Elf2.zig b/src/link/Elf2.zig index 194da884e8..46a5106046 100644 --- a/src/link/Elf2.zig +++ b/src/link/Elf2.zig @@ -4273,10 +4273,13 @@ fn flushFileOffset(elf: *Elf, ni: MappedFile.Node.Index) !void { }, .segment => |phndx| { switch (elf.phdrSlice()) { - inline else => |phdr| elf.targetStore( - &phdr[phndx].offset, - @intCast(ni.fileLocation(&elf.mf, false).offset), - ), + inline else => |phdr, class| { + const ph = &phdr[phndx]; + elf.targetStore(&ph.offset, @intCast(ni.fileLocation(&elf.mf, false).offset)); + if (elf.targetLoad(&ph.type) == .PHDR) { + @field(elf.ehdrPtr(), @tagName(class)).phoff = ph.offset; + } + }, } var child_it = ni.children(&elf.mf); while (child_it.next()) |child_ni| try elf.flushFileOffset(child_ni); @@ -4296,14 +4299,18 @@ fn flushMoved(elf: *Elf, ni: MappedFile.Node.Index) !void { .segment => |phndx| { try elf.flushFileOffset(ni); switch (elf.phdrSlice()) { - inline else => |phdr, class| { + inline else => |phdr| { const ph = &phdr[phndx]; switch (elf.targetLoad(&ph.type)) { else => unreachable, .NULL, .LOAD => return, - .DYNAMIC, .INTERP => {}, - .PHDR => @field(elf.ehdrPtr(), @tagName(class)).phoff = ph.offset, - .TLS, std.elf.PT.GNU_RELRO => {}, + + .DYNAMIC, + .INTERP, + .PHDR, + .TLS, + .GNU_RELRO, + => {}, } elf.targetStore(&ph.vaddr, @intCast(elf.computeNodeVAddr(ni))); ph.paddr = ph.vaddr;