{"record":{"id":"bef08889dd36e7a1","repo":"jdx/mise","slug":"cannot-relocate-rpath-must-grow-but-the-dynami","errorCode":null,"errorMessage":"cannot relocate {}: rpath must grow but the dynamic string table could not be located","messagePattern":"cannot relocate (.+?): rpath must grow but the dynamic string table could not be located","errorType":"exception","errorClass":"eyre::Report","httpStatus":null,"severity":"error","filePath":"src/system/packages/brew/elf.rs","lineNumber":304,"sourceCode":"        && let (Some(old), Some(new)) = (&old_rpath, &new_rpath_str)\n        && let (Some(st), Some(rp)) = (strtab_off, rpath_strtab_off)\n    {\n        let start = st + rp as usize;\n        content[start..start + new.len()].copy_from_slice(new.as_bytes());\n        for b in &mut content[start + new.len()..start + old.len()] {\n            *b = 0;\n        }\n    }\n    if interp_in_place && rpath_in_place {\n        return Ok(true);\n    }\n\n    // grow: append a new PT_LOAD holding the relocated program header table\n    // plus whichever strings no longer fit (patchelf's approach)\n    let move_interp = !interp_in_place;\n    let move_dynstr = !rpath_in_place;\n    if move_dynstr && (strtab_off.is_none() || strsz.is_none() || strsz_val_off.is_none()) {\n        bail!(\n            \"cannot relocate {}: rpath must grow but the dynamic string table \\\n             could not be located\",\n            path.display()\n        );\n    }\n\n    let align = phdrs\n        .iter()\n        .filter(|p| p.p_type == PT_LOAD)\n        .map(|p| p.p_align)\n        .max()\n        .unwrap_or(0x1000)\n        .max(0x10000); // covers 4K/16K/64K runtime page sizes\n    let new_off = round_up(content.len() as u64, align);\n    let max_vaddr_end = phdrs\n        .iter()\n        .filter(|p| p.p_type == PT_LOAD)\n        .map(|p| p.p_vaddr + p.p_memsz)","sourceCodeStart":286,"sourceCodeEnd":322,"githubUrl":"https://github.com/jdx/mise/blob/9dcfcaa0dc8747a2577d3270b69bb9d8313b2807/src/system/packages/brew/elf.rs#L286-L322","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["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","Verify the binary with readelf -d — if DT_STRTAB/DT_STRSZ are genuinely absent, report the bottle upstream","Fall back to pouring that formula with upstream Homebrew's PatchELF-based flow"],"exampleFix":"# before: long prefix forces rpath to grow\nmise settings brew.prefix ~/dev/tools/sandbox/homebrew  # 33 chars > 20-char placeholder\n\n# after: short prefix keeps replacements in place\nmise settings brew.prefix /home/linuxbrew/.linuxbrew  # same length as build prefix","handlingStrategy":"try-catch","validationCode":"// Pre-flight the grow-path prerequisites: a dynamic section exposing\n// DT_STRTAB and DT_STRSZ, with the strtab vaddr inside a PT_LOAD.\nfn grow_path_possible(content: &[u8]) -> bool {\n    // parse PT_DYNAMIC entries; require DT_STRTAB (5) and DT_STRSZ (10)\n    // both present before DT_NULL (0), and strtab vaddr mappable to an offset\n    parse_dynamic_has(content, &[5, 10]) // simplified predicate\n}","typeGuard":null,"tryCatchPattern":"// Fail the pour with an actionable message including the path, and suggest\n// a shorter prefix (the practical fix when rpath must grow).\nmatch elf::patch(&mut content, &opts, &path) {\n    Err(e) if e.to_string().contains(\"dynamic string table\") => {\n        return Err(e.wrap_err(\"rpath needs to grow; try a brew prefix no longer than /home/linuxbrew/.linuxbrew\"))\n    }\n    other => other?,\n}","preventionTips":["Install the brew prefix at a path length at or below the bottle build prefix so string growth never triggers","When supporting arbitrary prefixes, pre-scan binaries for DT_STRTAB/DT_STRSZ before committing to the pour"],"tags":["brew","elf","linux","rpath","relocation","binary-patching"],"backgroundTag":"binary-relocation-failed","analyzedSha":"9dcfcaa0dc8747a2577d3270b69bb9d8313b2807","analyzedAt":"2026-08-17T14:28:50.624Z","schemaVersion":2},"datasetVersion":"2026-08-21T18:17:14.833Z"}