jdx/mise · error

ELF uses PN_XNUM program header counts

Error message

ELF uses PN_XNUM program header counts

What it means

ELF's extended section numbering (PN_XNUM) stores the real program header count in section header 0 rather than e_phnum, and e_phnum then holds the escape value 0xffff. This parser does not implement PN_XNUM, so any binary reporting e_phnum >= 0xffff is rejected rather than misparsed. Such binaries are extremely rare (they need ~65k program headers).

Solutions

  1. Confirm the binary is not corrupted (readelf -h shows a sane program header count); re-extract if corrupted.
  2. For genuine PN_XNUM binaries, fall back to an external tool (patchelf) instead of this parser.
  3. Skip patching such files and record them as unsupported in the relocation flow.
Defensive patterns

Strategy: type-guard

Validate before calling

let e_phnum = u16::from_le_bytes([content[56], content[57]]);
if e_phnum >= 0xffff { /* PN_XNUM binary: skip or use external patchelf */ }

Type guard

fn uses_pn_xnum(elf: &[u8]) -> bool {
    elf.len() > 58 && u16::from_le_bytes([elf[56], elf[57]]) == 0xffff
}

Try / catch

match read_phdrs(&content) {
    Err(e) if e.to_string().contains("PN_XNUM") => {
        // route to patchelf or skip with a recorded warning
    }
    other => other?,
}

Prevention

When it happens

Trigger: Calling elf::patch or read_linkage on an ELF whose e_phnum field is 0xffff or larger, i.e. a PN_XNUM-encoded binary or a corrupted file with a garbage program-header count.

Common situations: Processing machine-generated or fuzzed binaries; a corrupted bottle; (rarely) a deliberately huge binary built with tens of thousands of segments that uses extended numbering.

Understand the failure class

Background: UnsupportedOperationException and "is not supported" errors: when a library deliberately refuses a call — this error's family across 30 libraries.

Related errors


AI-assisted analysis of jdx/mise@533346cc37 (2026-09-17). Data as JSON: /api/errors/f67404801c6bd1a1. Report an issue: GitHub.

Appendix: 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> {
    phdrs

View on GitHub (pinned to 533346cc37)