commit fec502ec674e458a53e846abb875e6591c3eacfb (tree)
parent 3dfcba86b856cfcf2aaef9e4ca636c7b2d2f9276
Author: Matthew Lugg <mlugg@mlugg.co.uk>
Date: Wed, 20 May 2026 10:37:45 +0100
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
Diffstat:
1 file changed, 15 insertions(+), 8 deletions(-)
diff --git 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;