zig

fork of https://codeberg.org/ziglang/zig
Log | Files | Refs | README | LICENSE

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:
Msrc/link/Elf2.zig | 31+++++++++++++++++++++----------
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 {