jdx/mise · error · eyre::Report

cannot relocate {}: rpath must grow but the dynamic string t

Error message

cannot relocate {}: rpath must grow but the dynamic string table could not be located

What it means

When an rpath replacement no longer fits in its original slot, the patchelf-style strategy appends a new PT_LOAD with a rebuilt dynamic string table — which requires locating DT_STRTAB, DT_STRSZ, and the file offset of the strsz value in PT_DYNAMIC. If any is missing (or the strtab vaddr maps to no file offset), the grow path is impossible and the pour fails for that file.

Source

Thrown at src/system/packages/brew/elf.rs:304

        && let (Some(old), Some(new)) = (&old_rpath, &new_rpath_str)
        && let (Some(st), Some(rp)) = (strtab_off, rpath_strtab_off)
    {
        let start = st + rp as usize;
        content[start..start + new.len()].copy_from_slice(new.as_bytes());
        for b in &mut content[start + new.len()..start + old.len()] {
            *b = 0;
        }
    }
    if interp_in_place && rpath_in_place {
        return Ok(true);
    }

    // grow: append a new PT_LOAD holding the relocated program header table
    // plus whichever strings no longer fit (patchelf's approach)
    let move_interp = !interp_in_place;
    let move_dynstr = !rpath_in_place;
    if move_dynstr && (strtab_off.is_none() || strsz.is_none() || strsz_val_off.is_none()) {
        bail!(
            "cannot relocate {}: rpath must grow but the dynamic string table \
             could not be located",
            path.display()
        );
    }

    let align = phdrs
        .iter()
        .filter(|p| p.p_type == PT_LOAD)
        .map(|p| p.p_align)
        .max()
        .unwrap_or(0x1000)
        .max(0x10000); // covers 4K/16K/64K runtime page sizes
    let new_off = round_up(content.len() as u64, align);
    let max_vaddr_end = phdrs
        .iter()
        .filter(|p| p.p_type == PT_LOAD)
        .map(|p| p.p_vaddr + p.p_memsz)

View on GitHub (pinned to 9dcfcaa0dc)

Solutions

  1. Install the brew prefix at a path no longer than the bottle's build prefix (/home/linuxbrew/.linuxbrew, or a similarly short path) so strings fit in place
  2. Verify the binary with readelf -d — if DT_STRTAB/DT_STRSZ are genuinely absent, report the bottle upstream
  3. Fall back to pouring that formula with upstream Homebrew's PatchELF-based flow

Example fix

# before: long prefix forces rpath to grow
mise settings brew.prefix ~/dev/tools/sandbox/homebrew  # 33 chars > 20-char placeholder

# after: short prefix keeps replacements in place
mise settings brew.prefix /home/linuxbrew/.linuxbrew  # same length as build prefix
Defensive patterns

Strategy: try-catch

Validate before calling

// Pre-flight the grow-path prerequisites: a dynamic section exposing
// DT_STRTAB and DT_STRSZ, with the strtab vaddr inside a PT_LOAD.
fn grow_path_possible(content: &[u8]) -> bool {
    // parse PT_DYNAMIC entries; require DT_STRTAB (5) and DT_STRSZ (10)
    // both present before DT_NULL (0), and strtab vaddr mappable to an offset
    parse_dynamic_has(content, &[5, 10]) // simplified predicate
}

Try / catch

// Fail the pour with an actionable message including the path, and suggest
// a shorter prefix (the practical fix when rpath must grow).
match elf::patch(&mut content, &opts, &path) {
    Err(e) if e.to_string().contains("dynamic string table") => {
        return Err(e.wrap_err("rpath needs to grow; try a brew prefix no longer than /home/linuxbrew/.linuxbrew"))
    }
    other => other?,
}

Prevention

When it happens

Trigger: elf::patch grow path: move_dynstr && (strtab_off.is_none() || strsz.is_none() || strsz_val_off.is_none()). Happens when a Linux bottle binary has DT_RPATH/DT_RUNPATH but no usable DT_STRTAB/DT_STRSZ entries before DT_NULL, or its vaddr does not fall inside a PT_LOAD — combined with a target prefix longer than the '@@HOMEBREW_PREFIX@@' placeholder so in-place replacement cannot fit.

Common situations: Installing the brew prefix under a long path (deep home directory) so every replacement string grows; binaries produced by exotic linkers (musl custom flags, BOLT-rewritten, prelinked) with unusual dynamic sections.

Related errors


AI-assisted analysis of jdx/mise@9dcfcaa0dc (2026-08-17). Data as JSON: /api/errors/bef08889dd36e7a1. Report an issue: GitHub.