authorgravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2026-08-13 20:56:49+01:00
committergravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2026-08-16 10:37:19+01:00
logbd805e82b9984bc388217415d2a814550d4033a8
treef4127757ff5d9a2448538e0b166bdd3de833944e
parent594a6d5665fba5696903043bbcb76a8ce8d28d0b
signaturelock-open Commit is signed but in an unrecognized format.

Elf2: populate fixed bits in SPARC relocations


1 files changed, 45 insertions(+), 11 deletions(-)

src/link/Elf2.zig+45-11
...@@ -135,6 +135,18 @@ inputs: std.ArrayList(struct {...@@ -135,6 +135,18 @@ inputs: std.ArrayList(struct {
135input_pending_index: u32,135input_pending_index: u32,
136input_sections: std.ArrayList(InputSection),136input_sections: std.ArrayList(InputSection),
137input_section_pending_index: u32,137input_section_pending_index: u32,
138/// SPARC has some weird relocations which involve setting some bits to fixed constant values. When
139/// we encounter such a relocation, we queue the action here, and apply them during `idle`.
140one_shot_fixups: std.ArrayList(struct {
141 node: MappedFile.Node.Index,
142 offset: u64,
143 /// The syntax in these tag names matches the syntax used in `SymbolReloc.Type.Simple.dest`.
144 action: enum {
145 @"32[12:10] = 0b000",
146 @"32[12:10] = 0b111",
147 @"32[12:12] = 0b0",
148 },
149}),
138navs: std.array_hash_map.Auto(InternPool.Nav.Index, struct {150navs: std.array_hash_map.Auto(InternPool.Nav.Index, struct {
139 lsi: Symbol.LocalIndex,151 lsi: Symbol.LocalIndex,
140 /// The start index of the contiguous sequence of symbol relocations in this NAV.152 /// The start index of the contiguous sequence of symbol relocations in this NAV.
...@@ -3356,6 +3368,7 @@ fn create(...@@ -3356,6 +3368,7 @@ fn create(
3356 .input_pending_index = 0,3368 .input_pending_index = 0,
3357 .input_sections = .empty,3369 .input_sections = .empty,
3358 .input_section_pending_index = 0,3370 .input_section_pending_index = 0,
3371 .one_shot_fixups = .empty,
3359 .navs = .empty,3372 .navs = .empty,
3360 .uavs = .empty,3373 .uavs = .empty,
3361 .lazy = comptime .initFill(.{3374 .lazy = comptime .initFill(.{
...@@ -3405,6 +3418,7 @@ pub fn deinit(elf: *Elf) void {...@@ -3405,6 +3418,7 @@ pub fn deinit(elf: *Elf) void {
3405 for (elf.inputs.items) |input| if (input.member) |m| gpa.free(m);3418 for (elf.inputs.items) |input| if (input.member) |m| gpa.free(m);
3406 elf.inputs.deinit(gpa);3419 elf.inputs.deinit(gpa);
3407 elf.input_sections.deinit(gpa);3420 elf.input_sections.deinit(gpa);
3421 elf.one_shot_fixups.deinit(gpa);
3408 elf.navs.deinit(gpa);3422 elf.navs.deinit(gpa);
3409 elf.uavs.deinit(gpa);3423 elf.uavs.deinit(gpa);
3410 for (&elf.lazy.values) |*lazy| lazy.map.deinit(gpa);3424 for (&elf.lazy.values) |*lazy| lazy.map.deinit(gpa);
...@@ -6615,36 +6629,36 @@ fn addRelocAssumeCapacity(...@@ -6615,36 +6629,36 @@ fn addRelocAssumeCapacity(
66156629
6616 // The following relocations are all represented by the ABI as writing to a 13 bit6630 // The following relocations are all represented by the ABI as writing to a 13 bit
6617 // field (32[12:0]), but masking out some bits of the value. To simplify our logic6631 // field (32[12:0]), but masking out some bits of the value. To simplify our logic
6618 // for applying relocations, we instead [un]set any fixed bits right now, then model6632 // for applying relocations, we split this action up: we create a relocation writing
6619 // the relocation as only writing to a smaller 10--12 bit field.6633 // to the 10--12 bit long field which is actually variable, and queue a one-shot
6620 // TODO: because we flush input sections lazily, we can't actually write these bits6634 // task to set the constant bits. We can't just write the bits now unfortunately
6621 // immediately---we'll instead have to queue the writes somehow.6635 // because they may be in an input section which has not yet been loaded.
6622 .PC10 => {6636 .PC10 => {
6623 // TODO: 32[12:10] = 0b0006637 try elf.one_shot_fixups.append(elf.base.comp.gpa, .{ .node = node, .offset = offset, .action = .@"32[12:10] = 0b000" });
6624 try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.rel, .{ .dest = .@"32[9:0]", .cast = .trunc, .shift = .@"0" }));6638 try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.rel, .{ .dest = .@"32[9:0]", .cast = .trunc, .shift = .@"0" }));
6625 },6639 },
6626 .L44 => {6640 .L44 => {
6627 // TODO: 32[12:12] = 0b06641 try elf.one_shot_fixups.append(elf.base.comp.gpa, .{ .node = node, .offset = offset, .action = .@"32[12:12] = 0b0" });
6628 try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.abs, .{ .dest = .@"32[11:0]", .cast = .trunc, .shift = .@"0" }));6642 try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.abs, .{ .dest = .@"32[11:0]", .cast = .trunc, .shift = .@"0" }));
6629 },6643 },
6630 .TLS_LDO_LOX10 => {6644 .TLS_LDO_LOX10 => {
6631 // TODO: 32[12:10] = 0b0006645 try elf.one_shot_fixups.append(elf.base.comp.gpa, .{ .node = node, .offset = offset, .action = .@"32[12:10] = 0b000" });
6632 try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.dtpoff, .{ .dest = .@"32[9:0]", .cast = .trunc, .shift = .@"0" }));6646 try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.dtpoff, .{ .dest = .@"32[9:0]", .cast = .trunc, .shift = .@"0" }));
6633 },6647 },
6634 .TLS_LE_LOX10 => {6648 .TLS_LE_LOX10 => {
6635 // TODO: 32[12:10] = 0b1116649 try elf.one_shot_fixups.append(elf.base.comp.gpa, .{ .node = node, .offset = offset, .action = .@"32[12:10] = 0b111" });
6636 try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.tpoff, .{ .dest = .@"32[9:0]", .cast = .trunc, .shift = .@"0" }));6650 try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.tpoff, .{ .dest = .@"32[9:0]", .cast = .trunc, .shift = .@"0" }));
6637 },6651 },
6638 .GOT10 => {6652 .GOT10 => {
6639 // TODO: 32[12:10] = 0b0006653 try elf.one_shot_fixups.append(elf.base.comp.gpa, .{ .node = node, .offset = offset, .action = .@"32[12:10] = 0b000" });
6640 elf.addGotRelocAssumeCapacity(node, offset, .{ .symbol = target }, addend, .simple(.offset, .{ .dest = .@"32[9:0]", .cast = .trunc, .shift = .@"0" }));6654 elf.addGotRelocAssumeCapacity(node, offset, .{ .symbol = target }, addend, .simple(.offset, .{ .dest = .@"32[9:0]", .cast = .trunc, .shift = .@"0" }));
6641 },6655 },
6642 .TLS_GD_LO10 => {6656 .TLS_GD_LO10 => {
6643 // TODO: 32[12:10] = 0b0006657 try elf.one_shot_fixups.append(elf.base.comp.gpa, .{ .node = node, .offset = offset, .action = .@"32[12:10] = 0b000" });
6644 elf.addGotRelocAssumeCapacity(node, offset, .{ .tlsgd0 = target }, addend, .simple(.offset, .{ .dest = .@"32[9:0]", .cast = .trunc, .shift = .@"0" }));6658 elf.addGotRelocAssumeCapacity(node, offset, .{ .tlsgd0 = target }, addend, .simple(.offset, .{ .dest = .@"32[9:0]", .cast = .trunc, .shift = .@"0" }));
6645 },6659 },
6646 .TLS_LDM_LO10 => {6660 .TLS_LDM_LO10 => {
6647 // TODO: 32[12:10] = 0b0006661 try elf.one_shot_fixups.append(elf.base.comp.gpa, .{ .node = node, .offset = offset, .action = .@"32[12:10] = 0b000" });
6648 elf.addGotRelocAssumeCapacity(node, offset, .tlsld0, addend, .simple(.offset, .{ .dest = .@"32[9:0]", .cast = .trunc, .shift = .@"0" }));6662 elf.addGotRelocAssumeCapacity(node, offset, .tlsld0, addend, .simple(.offset, .{ .dest = .@"32[9:0]", .cast = .trunc, .shift = .@"0" }));
6649 },6663 },
6650 },6664 },
...@@ -7391,6 +7405,25 @@ pub fn idle(elf: *Elf, tid: Zcu.PerThread.Id) link.Error!bool {...@@ -7391,6 +7405,25 @@ pub fn idle(elf: *Elf, tid: Zcu.PerThread.Id) link.Error!bool {
7391 };7405 };
7392 break :task;7406 break :task;
7393 }7407 }
7408 if (elf.one_shot_fixups.items.len > 0) {
7409 // Each of these is very simple, so an unreasonable amount of overhead would be
7410 // introduced if we only did one per `idle` call. Also, there is no risk of this work
7411 // being invalidated. So let's just flush the entire queue at once.
7412 for (elf.one_shot_fixups.items) |isw| {
7413 const dest_slice = isw.node.slice(&elf.mf)[isw.offset..][0..4];
7414 const old: u32 = std.mem.readInt(u32, dest_slice, elf.targetEndian());
7415 const new: u32 = switch (isw.action) {
7416 // zig fmt: off
7417 .@"32[12:10] = 0b000" => old & 0b11111111_11111111_11100011_11111111,
7418 .@"32[12:10] = 0b111" => old | 0b00000000_00000000_00011100_00000000,
7419 .@"32[12:12] = 0b0" => old & 0b11111111_11111111_11101111_11111111,
7420 // zig fmt: on
7421 };
7422 std.mem.writeInt(u32, dest_slice, new, elf.targetEndian());
7423 }
7424 elf.one_shot_fixups.clearRetainingCapacity();
7425 break :task;
7426 }
7394 if (elf.changed_symtab_index.pop()) |kv| {7427 if (elf.changed_symtab_index.pop()) |kv| {
7395 const sub_prog_node = elf.mf.update_prog_node.start(kv.key.slice(elf), 0);7428 const sub_prog_node = elf.mf.update_prog_node.start(kv.key.slice(elf), 0);
7396 defer sub_prog_node.end();7429 defer sub_prog_node.end();
...@@ -7481,6 +7514,7 @@ pub fn idle(elf: *Elf, tid: Zcu.PerThread.Id) link.Error!bool {...@@ -7481,6 +7514,7 @@ pub fn idle(elf: *Elf, tid: Zcu.PerThread.Id) link.Error!bool {
7481 }7514 }
7482 }7515 }
7483 if (elf.input_sections.items.len > elf.input_section_pending_index) return true;7516 if (elf.input_sections.items.len > elf.input_section_pending_index) return true;
7517 if (elf.one_shot_fixups.items.len > 0) return true;
7484 if (elf.changed_symtab_index.count() > 0) return true;7518 if (elf.changed_symtab_index.count() > 0) return true;
7485 if (elf.mf.updates.items.len > 0) return true;7519 if (elf.mf.updates.items.len > 0) return true;
7486 return false;7520 return false;