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
This commit is contained in:
Matthew Lugg
2026-05-20 10:37:45 +01:00
parent 3dfcba86b8
commit fec502ec67
+15 -8
View File
@@ -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;