Fix library collision in Termux by isolating binaries in opt/agy/bin#15
Conversation
Moving binaries to a dedicated directory (opt/agy/bin) and symlinking the wrapper prevents the relative library search from hitting Termux's dummy libraries in /usr/lib.
There was a problem hiding this comment.
Code Review
This pull request updates the installation script to install the agy and agy.va39 binaries into an internal directory (INTERNAL_BIN_DIR) and creates a symlink for agy in the public binary directory (INSTALL_BIN_DIR) to prevent RPATH collisions. A critical issue was identified where the backup and rollback mechanism fails during upgrades because it only backs up the symlink rather than the actual binaries, potentially leaving users with a broken installation if the upgrade fails. A layout-aware backup and rollback solution has been suggested.
Important
The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.
| mkdir -p "$INTERNAL_BIN_DIR" || die "Failed to create directory $INTERNAL_BIN_DIR" | ||
| install -m 0755 "$EXTRACT_DIR/bin/agy" "$INTERNAL_BIN_DIR/agy" || die "Failed to install agy binary to $INTERNAL_BIN_DIR" | ||
| install -m 0755 "$EXTRACT_DIR/bin/agy.va39" "$INTERNAL_BIN_DIR/agy.va39" || die "Failed to install agy.va39 binary to $INTERNAL_BIN_DIR" | ||
| ln -sf "$INTERNAL_BIN_DIR/agy" "$INSTALL_BIN_DIR/agy" || die "Failed to create symlink in $INSTALL_BIN_DIR" | ||
| # Ensure the secondary binary is not in the system bin to avoid RPATH collision | ||
| [[ -f "$INSTALL_BIN_DIR/agy.va39" ]] && rm -f "$INSTALL_BIN_DIR/agy.va39" | ||
| rm -rf "$EXTRACT_DIR" |
There was a problem hiding this comment.
Issue: Broken Backup and Rollback during Upgrades
When upgrading from a previous installation that already uses the new layout:
$INSTALL_BIN_DIR/agyis a symbolic link pointing to$INTERNAL_BIN_DIR/agy.- The backup logic on lines 340-343 checks
[[ -f "$INSTALL_BIN_DIR/agy" ]](which resolves to true because the symlink target exists) and moves the symlink itself to$AGY_BAK. - The actual binaries in
$INTERNAL_BIN_DIR/agyand$INTERNAL_BIN_DIR/agy.va39are not backed up. - The installer then overwrites the real binaries in
$INTERNAL_BIN_DIRwith the new versions. - If the installation fails or is cancelled later (e.g., during verification or the version test), the
cleanupfunction attempts to restore$AGY_BAKto$INSTALL_BIN_DIR/agy. This merely restores the old symlink pointing to the newly overwritten (and potentially broken/failed) binary in$INTERNAL_BIN_DIR, leaving the user with a broken installation.
Solution:
Redefine the cleanup function dynamically within this block to be layout-aware, back up the internal binaries if they exist, and ensure they are correctly restored or cleaned up on failure.
INTERNAL_AGY_BAK=""
INTERNAL_AGY_VA39_BAK=""
cleanup() {
printf "\033[?25h"
[[ -n "${TMP_LOGO:-}" && -f "$TMP_LOGO" ]] && rm -f "$TMP_LOGO"
[[ -n "${GLIBC_LOG:-}" && -f "$GLIBC_LOG" ]] && rm -f "$GLIBC_LOG"
[[ -n "${GLIBC_PCT:-}" && -f "$GLIBC_PCT" ]] && rm -f "$GLIBC_PCT"
[[ -d "$EXTRACT_DIR" ]] && rm -rf "$EXTRACT_DIR"
if [[ "${INSTALL_SUCCESS:-0}" -ne 1 ]]; then
[[ -f "$TMP" ]] && rm -f "$TMP"
if [[ -n "${AGY_BAK:-}" && -f "$AGY_BAK" && -z "${INTERNAL_AGY_BAK:-}" ]]; then
mv -f "$AGY_BAK" "$INSTALL_BIN_DIR/agy" || true
[[ -n "${AGY_VA39_BAK:-}" && -f "$AGY_VA39_BAK" ]] && mv -f "$AGY_VA39_BAK" "$INSTALL_BIN_DIR/agy.va39" || true
elif [[ -n "${INTERNAL_AGY_BAK:-}" && -f "$INTERNAL_AGY_BAK" ]]; then
rm -f "$INSTALL_BIN_DIR/agy" "$INSTALL_BIN_DIR/agy.va39"
mv -f "$INTERNAL_AGY_BAK" "$INTERNAL_BIN_DIR/agy" || true
[[ -n "${INTERNAL_AGY_VA39_BAK:-}" && -f "$INTERNAL_AGY_VA39_BAK" ]] && mv -f "$INTERNAL_AGY_VA39_BAK" "$INTERNAL_BIN_DIR/agy.va39" || true
ln -sf "$INTERNAL_BIN_DIR/agy" "$INSTALL_BIN_DIR/agy" || true
[[ -n "${AGY_BAK:-}" && -f "$AGY_BAK" ]] && rm -f "$AGY_BAK" || true
else
rm -f "$INSTALL_BIN_DIR/agy" "$INSTALL_BIN_DIR/agy.va39"
rm -f "$INTERNAL_BIN_DIR/agy" "$INTERNAL_BIN_DIR/agy.va39"
rmdir "$INTERNAL_BIN_DIR" 2>/dev/null || true
fi
else
[[ -n "${AGY_BAK:-}" && -f "$AGY_BAK" ]] && rm -f "$AGY_BAK" || true
[[ -n "${AGY_VA39_BAK:-}" && -f "$AGY_VA39_BAK" ]] && rm -f "$AGY_VA39_BAK" || true
[[ -n "${INTERNAL_AGY_BAK:-}" && -f "$INTERNAL_AGY_BAK" ]] && rm -f "$INTERNAL_AGY_BAK" || true
[[ -n "${INTERNAL_AGY_VA39_BAK:-}" && -f "$INTERNAL_AGY_VA39_BAK" ]] && rm -f "$INTERNAL_AGY_VA39_BAK" || true
fi
}
mkdir -p "$INTERNAL_BIN_DIR" || die "Failed to create directory $INTERNAL_BIN_DIR"
if [[ -f "$INTERNAL_BIN_DIR/agy" ]]; then
INTERNAL_AGY_BAK="$INTERNAL_BIN_DIR/agy.bak.$$"
mv -f "$INTERNAL_BIN_DIR/agy" "$INTERNAL_AGY_BAK" || die "Failed to back up existing agy binary from $INTERNAL_BIN_DIR"
fi
if [[ -f "$INTERNAL_BIN_DIR/agy.va39" ]]; then
INTERNAL_AGY_VA39_BAK="$INTERNAL_BIN_DIR/agy.va39.bak.$$"
mv -f "$INTERNAL_BIN_DIR/agy.va39" "$INTERNAL_AGY_VA39_BAK" || die "Failed to back up existing agy.va39 binary from $INTERNAL_BIN_DIR"
fi
install -m 0755 "$EXTRACT_DIR/bin/agy" "$INTERNAL_BIN_DIR/agy" || die "Failed to install agy binary to $INTERNAL_BIN_DIR"
install -m 0755 "$EXTRACT_DIR/bin/agy.va39" "$INTERNAL_BIN_DIR/agy.va39" || die "Failed to install agy.va39 binary to $INTERNAL_BIN_DIR"
ln -sf "$INTERNAL_BIN_DIR/agy" "$INSTALL_BIN_DIR/agy" || die "Failed to create symlink in $INSTALL_BIN_DIR"
[[ -f "$INSTALL_BIN_DIR/agy.va39" ]] && rm -f "$INSTALL_BIN_DIR/agy.va39"
rm -rf "$EXTRACT_DIR"|
Human here. Re:
not exactly, they before this PR land in some relative to |
@Manamama Thanks! Since we've figured out that the official I'll do some local testing first and see what I come up with before making any changes to the repo, and I want to hold on merging anything that might impact anyone currently using this that would break their |
Problem
The current installer places both
agy(wrapper) andagy.va39(glibc binary) directly into${PREFIX}/bin/.The
agywrapper resolves its location and adds../libto the library search path.In Termux,
${PREFIX}/bin/../libis${PREFIX}/lib/, which contains dummy placeholder libraries (likelibpthread.so.0) used for Android compatibility.These files are linker scripts (a few bytes long), causing the glibc loader to fail with:
error while loading shared libraries: .../lib/libpthread.so.0: file too shortSolution
This PR modifies the installer to:
${PREFIX}/opt/agy/bin/.${PREFIX}/bin/agypointing to the wrapper.agy.va39exists in the systembin/directory.Since the wrapper resolves its real path via
readlink("/proc/self/exe"), it will look for libraries in${PREFIX}/opt/agy/lib/. Since that directory is empty, it correctly falls back to the glibc system libraries in${PREFIX}/glibc/lib/, avoiding the collision with Termux's system libraries.This fix directly addresses the failures reported in issue #10.