commit bdc2b2846d3f8f3cd81b50cddb56a3a6f3f29485 (tree)
parent 2f9cae345a309bfc28b0cc09bc57247fe0f07a9a
Author: Casey Banner <kcbanner@gmail.com>
Date: Thu, 21 May 2026 08:27:15 +0100
MappedFile: implement `realign`
Note from mlugg: this commit is a stripped-down version of Casey's
implementation of `realign` from his WIP COFF linker branch. I've read
through the code, and while it's a bit inefficient in some cases, it
should work fine (and indeed does seem okay on this branch). I expect
that Casey will end up replacing this with his full implementation when
he opens a PR with his work.
Diffstat:
2 files changed, 91 insertions(+), 7 deletions(-)
diff --git a/src/link/Elf2.zig b/src/link/Elf2.zig
@@ -2947,7 +2947,7 @@ fn uavMapIndex(
} else {
const node = uav_gop.value_ptr.lsi.index().ptr(elf).node;
if (resolved_align.toStdMem().order(node.alignment(&elf.mf)).compare(.gt)) {
- node.realign(&elf.mf, resolved_align.toStdMem());
+ try node.realign(&elf.mf, gpa, resolved_align.toStdMem());
}
}
return umi;
diff --git a/src/link/MappedFile.zig b/src/link/MappedFile.zig
@@ -305,12 +305,20 @@ pub const Node = extern struct {
}
}
- pub fn realign(ni: Node.Index, mf: *MappedFile, new_alignment: std.mem.Alignment) void {
- ni.get(mf).flags.alignment = new_alignment;
-
- const old_offset, const old_size = ni.location(mf).resolve(mf);
- if (!new_alignment.check(@intCast(old_offset)) or !new_alignment.check(@intCast(old_size))) {
- @panic("TODO MappedFile.realign");
+ /// Moves and expands a node such that its offset and size are aligned to `new_alignment`.
+ ///
+ /// Asserts that `ni` is not `Node.Index.root`.
+ pub fn realign(
+ ni: Node.Index,
+ mf: *MappedFile,
+ gpa: std.mem.Allocator,
+ new_alignment: std.mem.Alignment,
+ ) !void {
+ try mf.realignNode(gpa, ni, new_alignment);
+ var writers_it = mf.writers.first;
+ while (writers_it) |writer_node| : (writers_it = writer_node.next) {
+ const w: *Node.Writer = @fieldParentPtr("writer_node", writer_node);
+ w.interface.buffer = w.ni.slice(mf);
}
}
@@ -852,6 +860,82 @@ fn resizeNode(mf: *MappedFile, gpa: std.mem.Allocator, ni: Node.Index, requested
}
}
+fn realignNode(
+ mf: *MappedFile,
+ gpa: std.mem.Allocator,
+ ni: Node.Index,
+ new_alignment: std.mem.Alignment,
+) !void {
+ assert(ni != Node.Index.root); // currently unsupported
+
+ const node = ni.get(mf);
+ const old_offset, const size = node.location().resolve(mf);
+
+ assert(new_alignment.compare(.gt, node.flags.alignment));
+
+ defer if (std.debug.runtime_safety) mf.verify();
+
+ node.flags.alignment = new_alignment;
+
+ const new_size = node.flags.alignment.forward(@intCast(size));
+ if (new_alignment.check(@intCast(old_offset))) {
+ if (new_size > size) try mf.resizeNode(gpa, ni, new_size);
+ return;
+ }
+
+ _, const parent_size = node.parent.location(mf).resolve(mf);
+ const trailing_end = trailing_end: switch (node.next) {
+ .none => parent_size,
+ else => |next_ni| {
+ const next_offset, _ = next_ni.location(mf).resolve(mf);
+ break :trailing_end next_offset;
+ },
+ };
+
+ const forward_offset = new_alignment.forward(@intCast(old_offset));
+ if (forward_offset + new_size <= trailing_end) {
+ // Shift into the free space if possible
+ try mf.ensureCapacityForSetLocation(gpa);
+ if (node.flags.has_content) {
+ const old_file_offset = ni.fileLocation(mf, false).offset;
+ const new_file_offset = (old_file_offset - old_offset) + forward_offset;
+ if (new_file_offset < old_file_offset + size) {
+ @memmove(
+ mf.memory_map.memory[@intCast(new_file_offset)..][0..@intCast(size)],
+ mf.memory_map.memory[@intCast(old_file_offset)..][0..@intCast(size)],
+ );
+ } else try mf.moveRange(old_file_offset, new_file_offset, size);
+ @memset(mf.memory_map.memory[@intCast(new_file_offset + size)..][0..@intCast(new_size - size)], 0);
+ }
+
+ ni.setLocationAssumeCapacity(mf, forward_offset, new_size);
+ } else {
+ const temp_size = node.flags.alignment.forward(@intCast(new_size + 1));
+ try mf.resizeNode(gpa, ni, temp_size);
+ const new_offset, _ = ni.location(mf).resolve(mf);
+
+ try mf.ensureCapacityForSetLocation(gpa);
+
+ // Non-fixed nodes may now be aligned if the resize moved them
+ const new_forward_offset = new_alignment.forward(@intCast(new_offset));
+ const final_offset = if (new_forward_offset != new_offset) final_offset: {
+ if (node.flags.has_content) {
+ const old_file_offset = ni.fileLocation(mf, false).offset;
+ const new_file_offset = (old_file_offset - new_offset) + new_forward_offset;
+ @memmove(
+ mf.memory_map.memory[@intCast(new_file_offset)..][0..@intCast(size)],
+ mf.memory_map.memory[@intCast(old_file_offset)..][0..@intCast(size)],
+ );
+ @memset(mf.memory_map.memory[@intCast(old_file_offset)..@intCast(new_file_offset)], 0);
+ }
+
+ break :final_offset new_forward_offset;
+ } else new_offset;
+
+ ni.setLocationAssumeCapacity(mf, final_offset, new_size);
+ }
+}
+
fn moveRange(mf: *MappedFile, old_file_offset: u64, new_file_offset: u64, size: u64) !void {
// make a copy of this node at the new location
try mf.copyRange(old_file_offset, new_file_offset, size);