jdx/mise · error
cannot relocate : rpath must grow but the dynamic string…
Error message
cannot relocate {}: rpath must grow but the dynamic string table could not be located What it means
When patching an ELF's rpath, the new rpath usually fits in the existing dynamic string table slot. If it must grow, the patcher relocates the dynamic string table into a newly appended PT_LOAD — but that requires knowing the string table's file offset (strtab_off), size (strsz), and the file offset of the DT_STRSZ value to rewrite. If the PT_DYNAMIC segment did not yield those (e.g. missing DT_STRTAB/DT_STRSZ entries, or the strtab vaddr could not be mapped to a file offset), the relocation cannot proceed safely and patch bails.
Solutions
- Verify the binary's dynamic section is well formed (readelf -d shows DT_STRTAB and DT_STRSZ); rebuild the binary with the standard linker if not.
- Re-download/re-extract the bottle — a corrupted dynamic section can hide DT entries.
- For such binaries, fall back to patchelf (which handles more ELF variants) or skip relocation for that file.
Defensive patterns
Strategy: try-catch
Validate before calling
// ensure dynamic string table metadata is present before requesting a grow-relocation
let dyn_ok = has_dt_strtab_and_strsz(&content); // scan PT_DYNAMIC for DT_STRTAB & DT_STRSZ
let will_grow = new_rpath_len > old_rpath_len;
if will_grow && !dyn_ok { /* use patchelf fallback or skip */ } Try / catch
match elf::patch(&mut content, &opts, &path) {
Err(e) if e.to_string().contains("dynamic string table") => {
warn!("{}: cannot grow rpath in place, falling back to patchelf", path.display());
}
other => other?,
} Prevention
- Only relocate binaries with well-formed PT_DYNAMIC segments (DT_STRTAB + DT_STRSZ present and mappable).
- Prefer shorter install prefixes when bottling so new rpaths fit existing string slots.
- Keep patchelf available as a fallback for exotic or packed binaries.
When it happens
Trigger: Calling elf::patch on a binary whose new rpath is longer than the old one (so it cannot be written in place) while the dynamic section lacks DT_STRTAB or DT_STRSZ entries, or the DT_STRTAB vaddr does not fall inside any PT_LOAD segment (so vaddr_to_offset returns None).
Common situations: Relinking an unusual binary (stripped, packed, or produced by a linker that omits DT_STRSZ or uses odd vaddr mapping) whose bottle placeholder rpath expands to a longer path under the new prefix; relocating binaries with malformed dynamic sections.
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
- unexpected ELF e_phentsize
- brew-cask: : only font-only casks without lifecycle hooks…
- cannot relocate : replacement for does not fit ( > bytes)
- local mise is not an ELF executable
- no dynamic linker found for
AI-assisted analysis of jdx/mise@533346cc37 (2026-09-17).
Data as JSON: /api/errors/bef08889dd36e7a1.
Report an issue: GitHub.
Appendix: 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 533346cc37)