authorgravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2026-05-28 12:14:19+01:00
committergravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2026-05-28 12:16:58+01:00
logee13aaeb8ddb8d00d4186c300b616b21da121963
tree9935c9764c2c1a60e8edd09ac6d7ef49a4e217e4
parenta7531ba928938029f2381b8ec5845c2797528b12
signaturelock-open Commit is signed but in an unrecognized format.

Elf2: prevent bogus relocations into debug sections

If a `.debug_*` section---or any non-alloc section for that matter!---references a symbol which is not resolved, we must not emit a dynamic relocation for it. Since a non-alloc section's base virtual address is 0, this could potentially cause crashes in rtld. I'm not sure what the correct behavior is here, but for now, this prevents a possible crash.

1 files changed, 21 insertions(+), 10 deletions(-)

src/link/Elf2.zig+21-10
...@@ -128,7 +128,7 @@ section_by_name: std.array_hash_map.Auto(String(.shstrtab), void),...@@ -128,7 +128,7 @@ section_by_name: std.array_hash_map.Auto(String(.shstrtab), void),
128changed_symtab_index: std.array_hash_map.Auto(String(.strtab), void),128changed_symtab_index: std.array_hash_map.Auto(String(.strtab), void),
129/// Counts how many relocations are currently in `.rela.dyn` which would require a `DT_TEXTREL`129/// Counts how many relocations are currently in `.rela.dyn` which would require a `DT_TEXTREL`
130/// entry in the `.dynamic` section. This allows adding `DT_TEXTREL` to the output `.dynamic`130/// entry in the `.dynamic` section. This allows adding `DT_TEXTREL` to the output `.dynamic`
131/// section in `flush` only when it is actually necessary. See also `nodeRequiresTextrel`.131/// section in `flush` only when it is actually necessary. See also `nodeWantsDsoRelocation`.
132textrel_count: u32,132textrel_count: u32,
133133
134const_prog_node: std.Progress.Node,134const_prog_node: std.Progress.Node,
...@@ -1128,8 +1128,10 @@ const SymbolReloc = struct {...@@ -1128,8 +1128,10 @@ const SymbolReloc = struct {
1128 }1128 }
1129 if (reloc.rela_index.unwrap()) |rela_index| {1129 if (reloc.rela_index.unwrap()) |rela_index| {
1130 reloc.relaSection(elf).relaDeleteOne(elf, rela_index);1130 reloc.relaSection(elf).relaDeleteOne(elf, rela_index);
1131 if (elf.nodeRequiresTextrel(reloc.node)) {1131 switch (elf.nodeWantsDsoRelocation(reloc.node)) {
1132 elf.textrel_count -= 1;1132 .no => unreachable, // there *was* a dynamic relocation!
1133 .yes => {},
1134 .yes_textrel => elf.textrel_count -= 1,
1133 }1135 }
1134 }1136 }
1135 if (reloc.type.dependsOnTlsSize()) {1137 if (reloc.type.dependsOnTlsSize()) {
...@@ -1672,8 +1674,10 @@ fn setGlobalSymbolValue(...@@ -1672,8 +1674,10 @@ fn setGlobalSymbolValue(
1672 assert(reloc.target == Symbol.Id.global(global_name));1674 assert(reloc.target == Symbol.Id.global(global_name));
1673 if (reloc.rela_index.unwrap()) |rela_index| {1675 if (reloc.rela_index.unwrap()) |rela_index| {
1674 reloc.relaSection(elf).relaDeleteOne(elf, rela_index);1676 reloc.relaSection(elf).relaDeleteOne(elf, rela_index);
1675 if (elf.nodeRequiresTextrel(reloc.node)) {1677 switch (elf.nodeWantsDsoRelocation(reloc.node)) {
1676 elf.textrel_count -= 1;1678 .no => unreachable, // there *was* a dynamic relocation!
1679 .yes => {},
1680 .yes_textrel => elf.textrel_count -= 1,
1677 }1681 }
1678 reloc.rela_index = .none;1682 reloc.rela_index = .none;
1679 }1683 }
...@@ -5108,8 +5112,10 @@ fn addSymbolRelocAssumeCapacity(...@@ -5108,8 +5112,10 @@ fn addSymbolRelocAssumeCapacity(
5108 } else elf.globalByName(name).?.dynsym_index,5112 } else elf.globalByName(name).?.dynsym_index,
5109 };5113 };
51105114
5111 if (elf.nodeRequiresTextrel(node)) {5115 switch (elf.nodeWantsDsoRelocation(node)) {
5112 elf.textrel_count += 1;5116 .no => break :r .none,
5117 .yes => {},
5118 .yes_textrel => elf.textrel_count += 1,
5113 }5119 }
51145120
5115 // It currently looks like we need a runtime relocation for this.5121 // It currently looks like we need a runtime relocation for this.
...@@ -5383,13 +5389,18 @@ fn updateGotEntry(elf: *Elf, got_index: usize) void {...@@ -5383,13 +5389,18 @@ fn updateGotEntry(elf: *Elf, got_index: usize) void {
5383 };5389 };
5384}5390}
53855391
5386/// Returns whether a `DT_TEXTREL` dynamic entry is needed to have a runtime relocation in `node`.5392/// If `node` cannot contain runtime relocations, returns `.no`.
5387fn nodeRequiresTextrel(elf: *Elf, node: MappedFile.Node.Index) bool {5393///
5394/// If `node` can contain runtime relocations, `returns `.yes_textrel` if such a relocation requires
5395/// the presence of a `DT_TEXTREL` dynamic entry, or `.yes` otherwise.
5396fn nodeWantsDsoRelocation(elf: *Elf, node: MappedFile.Node.Index) enum { yes, yes_textrel, no } {
5388 const shndx = elf.getNodeShndx(node);5397 const shndx = elf.getNodeShndx(node);
5389 const shf: std.elf.SHF = switch (elf.shdrPtr(shndx)) {5398 const shf: std.elf.SHF = switch (elf.shdrPtr(shndx)) {
5390 inline else => |shdr| elf.targetLoad(&shdr.flags).shf,5399 inline else => |shdr| elf.targetLoad(&shdr.flags).shf,
5391 };5400 };
5392 return shf.ALLOC and !shf.WRITE;5401 if (!shf.ALLOC) return .no;
5402 if (!shf.WRITE) return .yes_textrel;
5403 return .yes;
5393}5404}
53945405
5395pub fn updateNav(elf: *Elf, pt: Zcu.PerThread, nav_index: InternPool.Nav.Index) !void {5406pub fn updateNav(elf: *Elf, pt: Zcu.PerThread, nav_index: InternPool.Nav.Index) !void {