commit ee13aaeb8ddb8d00d4186c300b616b21da121963 (tree)
parent a7531ba928938029f2381b8ec5845c2797528b12
Author: Matthew Lugg <mlugg@mlugg.co.uk>
Date: Thu, 28 May 2026 12:14:19 +0100
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.
Diffstat:
1 file changed, 21 insertions(+), 10 deletions(-)
diff --git a/src/link/Elf2.zig b/src/link/Elf2.zig
@@ -128,7 +128,7 @@ section_by_name: std.array_hash_map.Auto(String(.shstrtab), void),
changed_symtab_index: std.array_hash_map.Auto(String(.strtab), void),
/// Counts how many relocations are currently in `.rela.dyn` which would require a `DT_TEXTREL`
/// entry in the `.dynamic` section. This allows adding `DT_TEXTREL` to the output `.dynamic`
-/// section in `flush` only when it is actually necessary. See also `nodeRequiresTextrel`.
+/// section in `flush` only when it is actually necessary. See also `nodeWantsDsoRelocation`.
textrel_count: u32,
const_prog_node: std.Progress.Node,
@@ -1128,8 +1128,10 @@ const SymbolReloc = struct {
}
if (reloc.rela_index.unwrap()) |rela_index| {
reloc.relaSection(elf).relaDeleteOne(elf, rela_index);
- if (elf.nodeRequiresTextrel(reloc.node)) {
- elf.textrel_count -= 1;
+ switch (elf.nodeWantsDsoRelocation(reloc.node)) {
+ .no => unreachable, // there *was* a dynamic relocation!
+ .yes => {},
+ .yes_textrel => elf.textrel_count -= 1,
}
}
if (reloc.type.dependsOnTlsSize()) {
@@ -1672,8 +1674,10 @@ fn setGlobalSymbolValue(
assert(reloc.target == Symbol.Id.global(global_name));
if (reloc.rela_index.unwrap()) |rela_index| {
reloc.relaSection(elf).relaDeleteOne(elf, rela_index);
- if (elf.nodeRequiresTextrel(reloc.node)) {
- elf.textrel_count -= 1;
+ switch (elf.nodeWantsDsoRelocation(reloc.node)) {
+ .no => unreachable, // there *was* a dynamic relocation!
+ .yes => {},
+ .yes_textrel => elf.textrel_count -= 1,
}
reloc.rela_index = .none;
}
@@ -5108,8 +5112,10 @@ fn addSymbolRelocAssumeCapacity(
} else elf.globalByName(name).?.dynsym_index,
};
- if (elf.nodeRequiresTextrel(node)) {
- elf.textrel_count += 1;
+ switch (elf.nodeWantsDsoRelocation(node)) {
+ .no => break :r .none,
+ .yes => {},
+ .yes_textrel => elf.textrel_count += 1,
}
// It currently looks like we need a runtime relocation for this.
@@ -5383,13 +5389,18 @@ fn updateGotEntry(elf: *Elf, got_index: usize) void {
};
}
-/// Returns whether a `DT_TEXTREL` dynamic entry is needed to have a runtime relocation in `node`.
-fn nodeRequiresTextrel(elf: *Elf, node: MappedFile.Node.Index) bool {
+/// If `node` cannot contain runtime relocations, returns `.no`.
+///
+/// If `node` can contain runtime relocations, `returns `.yes_textrel` if such a relocation requires
+/// the presence of a `DT_TEXTREL` dynamic entry, or `.yes` otherwise.
+fn nodeWantsDsoRelocation(elf: *Elf, node: MappedFile.Node.Index) enum { yes, yes_textrel, no } {
const shndx = elf.getNodeShndx(node);
const shf: std.elf.SHF = switch (elf.shdrPtr(shndx)) {
inline else => |shdr| elf.targetLoad(&shdr.flags).shf,
};
- return shf.ALLOC and !shf.WRITE;
+ if (!shf.ALLOC) return .no;
+ if (!shf.WRITE) return .yes_textrel;
+ return .yes;
}
pub fn updateNav(elf: *Elf, pt: Zcu.PerThread, nav_index: InternPool.Nav.Index) !void {