jdx/mise · error · eyre::Report
ELF uses PN_XNUM program header counts
Error message
ELF uses PN_XNUM program header counts
What it means
The minimal ELF parser does not implement extended program header numbering (PN_XNUM): when e_phnum is 0xffff or more, the real count lives in sh_info of section header 0. Such binaries — only possible with 65535+ program headers — are rejected instead of misparsed.
Source
Thrown at src/system/packages/brew/elf.rs:117
#[derive(Clone, Copy)]
struct Phdr {
p_type: u32,
p_offset: u64,
p_vaddr: u64,
p_filesz: u64,
p_memsz: u64,
p_align: u64,
}
fn read_phdrs(content: &[u8]) -> Result<Vec<Phdr>> {
let e_phoff = rd_u64(content, 32)? as usize;
let e_phentsize = rd_u16(content, 54)? as usize;
let e_phnum = rd_u16(content, 56)? as usize;
if e_phentsize != PHDR_SIZE {
bail!("unexpected ELF e_phentsize {e_phentsize}");
}
if e_phnum >= 0xffff {
bail!("ELF uses PN_XNUM program header counts");
}
let mut phdrs = Vec::with_capacity(e_phnum);
for i in 0..e_phnum {
let off = e_phoff + i * PHDR_SIZE;
phdrs.push(Phdr {
p_type: rd_u32(content, off)?,
p_offset: rd_u64(content, off + 8)?,
p_vaddr: rd_u64(content, off + 16)?,
p_filesz: rd_u64(content, off + 32)?,
p_memsz: rd_u64(content, off + 40)?,
p_align: rd_u64(content, off + 48)?,
});
}
Ok(phdrs)
}
fn vaddr_to_offset(phdrs: &[Phdr], vaddr: u64) -> Option<usize> {
phdrsView on GitHub (pinned to 9dcfcaa0dc)
Solutions
- Pour the affected formula with upstream Homebrew (PatchELF/ruby handle PN_XNUM differently) instead of the internal rewriter
- Rebuild the binary with a normal number of program headers if you control its build
- Report upstream — PN_XNUM support would need to read the count from section header 0
Defensive patterns
Strategy: validation
Validate before calling
// Reject PN_XNUM binaries before calling the rewriter.
fn phnum_supported(content: &[u8]) -> bool {
content.len() >= 58
&& u16::from_le_bytes(content[56..58].try_into().unwrap()) < 0xffff
} Type guard
fn elf_patchable(content: &[u8]) -> bool {
is_elf(content) && content.len() >= 58 && content[4] == 2 && content[5] == 1
&& u16::from_le_bytes(content[54..56].try_into().unwrap()) == 56
&& u16::from_le_bytes(content[56..58].try_into().unwrap()) < 0xffff
} Try / catch
// Detect the unsupported extension and route the formula to a fallback
// pour path instead of failing the install.
if u16::from_le_bytes(content[56..58].try_into().unwrap()) >= 0xffff {
debug!("{}: PN_XNUM not supported, skipping internal relocation", path.display());
return Ok(false);
} Prevention
- Don't pour fuzzing corpora or research binaries with huge phdr counts through the internal rewriter
- Check e_phnum with readelf -h first when automating pours of untrusted binaries
When it happens
Trigger: elf::patch -> read_phdrs: e_phnum = rd_u16(content, 56) >= 0xffff while pouring a Linux bottle. Requires a pathological binary with an enormous program header table, e.g. custom-built test artifacts or fuzzing corpora; ordinary bottles never hit it.
Common situations: Pouring hand-crafted or fuzz-generated ELF binaries; binaries from research/experimental toolchains that emit thousands of segments.
Related errors
- unexpected ELF e_phentsize {e_phentsize}
- cannot relocate {}: rpath must grow but the dynamic string t
- malformed Mach-O in {}
- malformed load command table in {}
- malformed load command in {}
AI-assisted analysis of jdx/mise@9dcfcaa0dc (2026-08-17).
Data as JSON: /api/errors/f67404801c6bd1a1.
Report an issue: GitHub.