authorgravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2026-05-07 11:34:36+01:00
committergravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2026-05-17 18:08:58+01:00
log2ec0b886a28a6af0bea158e22d766c8f2c294c60
tree02a36b86cba72e4f74564372bfc6416787094d64
parent55cdf6c28ba80dec9b595ff6e735090d1c081b26
signaturelock-open Commit is signed but in an unrecognized format.

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.

1 files changed, 55 insertions(+), 8 deletions(-)

src/link/Elf2.zig+55-8
...@@ -2082,10 +2082,7 @@ fn loadObject(...@@ -2082,10 +2082,7 @@ fn loadObject(
20822082
2083 const ii: Node.InputIndex = @enumFromInt(elf.inputs.items.len);2083 const ii: Node.InputIndex = @enumFromInt(elf.inputs.items.len);
2084 log.debug("loadObject({f}{f})", .{ path.fmtEscapeString(), fmtMemberString(member) });2084 log.debug("loadObject({f}{f})", .{ path.fmtEscapeString(), fmtMemberString(member) });
2085 const ident = try r.peek(std.elf.EI.OSABI);2085 try elf.checkInputIdent(path, r);
2086 if (!std.mem.eql(u8, ident[0..std.elf.MAGIC.len], std.elf.MAGIC)) return error.BadMagic;
2087 if (!std.mem.eql(u8, ident[std.elf.MAGIC.len..], elf.mf.memory_map.memory[std.elf.MAGIC.len..ident.len]))
2088 return diags.failParse(path, "bad ident", .{});
2089 try elf.symtab.ensureUnusedCapacity(gpa, 1);2086 try elf.symtab.ensureUnusedCapacity(gpa, 1);
2090 try elf.inputs.ensureUnusedCapacity(gpa, 1);2087 try elf.inputs.ensureUnusedCapacity(gpa, 1);
2091 elf.inputs.addOneAssumeCapacity().* = .{2088 elf.inputs.addOneAssumeCapacity().* = .{
...@@ -2321,10 +2318,7 @@ fn loadDso(elf: *Elf, path: std.Build.Cache.Path, fr: *Io.File.Reader) !void {...@@ -2321,10 +2318,7 @@ fn loadDso(elf: *Elf, path: std.Build.Cache.Path, fr: *Io.File.Reader) !void {
2321 const r = &fr.interface;2318 const r = &fr.interface;
23222319
2323 log.debug("loadDso({f})", .{path.fmtEscapeString()});2320 log.debug("loadDso({f})", .{path.fmtEscapeString()});
2324 const ident = try r.peek(std.elf.EI.NIDENT);2321 try elf.checkInputIdent(path, r);
2325 if (!std.mem.eql(u8, ident[0..std.elf.MAGIC.len], std.elf.MAGIC)) return error.BadMagic;
2326 if (!std.mem.eql(u8, ident[std.elf.MAGIC.len..], elf.mf.memory_map.memory[std.elf.MAGIC.len..ident.len]))
2327 return diags.failParse(path, "bad ident", .{});
2328 const target_endian = elf.targetEndian();2322 const target_endian = elf.targetEndian();
2329 switch (elf.identClass()) {2323 switch (elf.identClass()) {
2330 .NONE, _ => unreachable,2324 .NONE, _ => unreachable,
...@@ -2395,6 +2389,59 @@ fn loadDsoExact(elf: *Elf, name: []const u8) !void {...@@ -2395,6 +2389,59 @@ fn loadDsoExact(elf: *Elf, name: []const u8) !void {
2395 try elf.needed.put(elf.base.comp.gpa, try elf.string(.dynstr, name), {});2389 try elf.needed.put(elf.base.comp.gpa, try elf.string(.dynstr, name), {});
2396}2390}
23972391
2392/// Validates that the `std.elf.Ident` present at the start of `r` is a compatible link input.
2393///
2394/// Returns an error if it is incompatible, or if the ident is broken or missing.
2395///
2396/// Does not advance the position of `r`. Requires `r` to have a 16-byte buffer.
2397fn checkInputIdent(
2398 elf: *const Elf,
2399 path: std.Build.Cache.Path,
2400 r: *Io.Reader,
2401) !void {
2402 const diags = &elf.base.comp.link_diags;
2403
2404 const ident = try r.peekStructPointer(std.elf.Ident);
2405 const target: *const std.elf.Ident = @ptrCast(elf.mf.memory_map.memory[0..@sizeOf(std.elf.Ident)]);
2406
2407 if (!std.mem.eql(u8, &ident.magic, std.elf.MAGIC)) {
2408 return error.BadMagic;
2409 }
2410
2411 if (ident.class != target.class) return diags.failParse(
2412 path,
2413 "bad ELF class ({?s})",
2414 .{std.enums.tagName(std.elf.CLASS, ident.class)},
2415 );
2416 if (ident.data != target.data) return diags.failParse(
2417 path,
2418 "bad ELF data encoding ({?s})",
2419 .{std.enums.tagName(std.elf.DATA, ident.data)},
2420 );
2421 if (ident.version != target.version) return diags.failParse(
2422 path,
2423 "bad ELF version ({d})",
2424 .{ident.version},
2425 );
2426
2427 // OSABI is a bit more complex. On Linux, `.NONE` and `.GNU` are both valid and both common.
2428 // It sounds reasonable to allow the value we chose *and* allow `.NONE`.
2429 const expect_abiversion: u8 = abiver: {
2430 if (ident.osabi == .NONE) break :abiver 0;
2431 if (ident.osabi == target.osabi) break :abiver target.abiversion;
2432 return diags.failParse(
2433 path,
2434 "bad ELF OS/ABI ({?s})",
2435 .{std.enums.tagName(std.elf.OSABI, ident.osabi)},
2436 );
2437 };
2438 if (ident.abiversion != expect_abiversion) return diags.failParse(
2439 path,
2440 "bad ELF ABI version ({d})",
2441 .{ident.abiversion},
2442 );
2443}
2444
2398pub fn prelink(elf: *Elf, prog_node: std.Progress.Node) !void {2445pub fn prelink(elf: *Elf, prog_node: std.Progress.Node) !void {
2399 _ = prog_node;2446 _ = prog_node;
2400 elf.prelinkInner() catch |err| switch (err) {2447 elf.prelinkInner() catch |err| switch (err) {