zig

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

commit 2ec0b886a28a6af0bea158e22d766c8f2c294c60 (tree)
parent 55cdf6c28ba80dec9b595ff6e735090d1c081b26
Author: Matthew Lugg <mlugg@mlugg.co.uk>
Date:   Thu,  7 May 2026 11:34:36 +0100

Elf2: allow more EI_OSABI values in inputs

On Linux, the OSABI field can be either ELFOSABI_GNU or ELFOSABI_NONE
(aka ELFOSABI_SYSV). Therefore, even if we have chosen ELFOSABI_GNU, we
still need to accept ELFOSABI_NONE in link inputs.

Then, since we're now having to check the ident componentwise anyway, we
may as well give more precise error messages on mismatch.

Diffstat:
Msrc/link/Elf2.zig | 63+++++++++++++++++++++++++++++++++++++++++++++++++++++++--------
1 file changed, 55 insertions(+), 8 deletions(-)

diff --git a/src/link/Elf2.zig b/src/link/Elf2.zig @@ -2082,10 +2082,7 @@ fn loadObject( const ii: Node.InputIndex = @enumFromInt(elf.inputs.items.len); log.debug("loadObject({f}{f})", .{ path.fmtEscapeString(), fmtMemberString(member) }); - const ident = try r.peek(std.elf.EI.OSABI); - if (!std.mem.eql(u8, ident[0..std.elf.MAGIC.len], std.elf.MAGIC)) return error.BadMagic; - if (!std.mem.eql(u8, ident[std.elf.MAGIC.len..], elf.mf.memory_map.memory[std.elf.MAGIC.len..ident.len])) - return diags.failParse(path, "bad ident", .{}); + try elf.checkInputIdent(path, r); try elf.symtab.ensureUnusedCapacity(gpa, 1); try elf.inputs.ensureUnusedCapacity(gpa, 1); elf.inputs.addOneAssumeCapacity().* = .{ @@ -2321,10 +2318,7 @@ fn loadDso(elf: *Elf, path: std.Build.Cache.Path, fr: *Io.File.Reader) !void { const r = &fr.interface; log.debug("loadDso({f})", .{path.fmtEscapeString()}); - const ident = try r.peek(std.elf.EI.NIDENT); - if (!std.mem.eql(u8, ident[0..std.elf.MAGIC.len], std.elf.MAGIC)) return error.BadMagic; - if (!std.mem.eql(u8, ident[std.elf.MAGIC.len..], elf.mf.memory_map.memory[std.elf.MAGIC.len..ident.len])) - return diags.failParse(path, "bad ident", .{}); + try elf.checkInputIdent(path, r); const target_endian = elf.targetEndian(); switch (elf.identClass()) { .NONE, _ => unreachable, @@ -2395,6 +2389,59 @@ fn loadDsoExact(elf: *Elf, name: []const u8) !void { try elf.needed.put(elf.base.comp.gpa, try elf.string(.dynstr, name), {}); } +/// Validates that the `std.elf.Ident` present at the start of `r` is a compatible link input. +/// +/// Returns an error if it is incompatible, or if the ident is broken or missing. +/// +/// Does not advance the position of `r`. Requires `r` to have a 16-byte buffer. +fn checkInputIdent( + elf: *const Elf, + path: std.Build.Cache.Path, + r: *Io.Reader, +) !void { + const diags = &elf.base.comp.link_diags; + + const ident = try r.peekStructPointer(std.elf.Ident); + const target: *const std.elf.Ident = @ptrCast(elf.mf.memory_map.memory[0..@sizeOf(std.elf.Ident)]); + + if (!std.mem.eql(u8, &ident.magic, std.elf.MAGIC)) { + return error.BadMagic; + } + + if (ident.class != target.class) return diags.failParse( + path, + "bad ELF class ({?s})", + .{std.enums.tagName(std.elf.CLASS, ident.class)}, + ); + if (ident.data != target.data) return diags.failParse( + path, + "bad ELF data encoding ({?s})", + .{std.enums.tagName(std.elf.DATA, ident.data)}, + ); + if (ident.version != target.version) return diags.failParse( + path, + "bad ELF version ({d})", + .{ident.version}, + ); + + // OSABI is a bit more complex. On Linux, `.NONE` and `.GNU` are both valid and both common. + // It sounds reasonable to allow the value we chose *and* allow `.NONE`. + const expect_abiversion: u8 = abiver: { + if (ident.osabi == .NONE) break :abiver 0; + if (ident.osabi == target.osabi) break :abiver target.abiversion; + return diags.failParse( + path, + "bad ELF OS/ABI ({?s})", + .{std.enums.tagName(std.elf.OSABI, ident.osabi)}, + ); + }; + if (ident.abiversion != expect_abiversion) return diags.failParse( + path, + "bad ELF ABI version ({d})", + .{ident.abiversion}, + ); +} + pub fn prelink(elf: *Elf, prog_node: std.Progress.Node) !void { _ = prog_node; elf.prelinkInner() catch |err| switch (err) {