archr: symlink PortMaster compat libs into the default linker path

PortMaster runtimes failed to launch: love_11.5's love.aarch64 died with
"error while loading shared libraries: libtheoradec.so.1: cannot open
shared object file" (caught live over SSH while a port was launched).

Root cause: ArchR keeps the ~50 PortMaster compat libraries (older
Debian-11-era SONAMEs: libtheoradec.so.1, libavcodec.so.58,
libwebp.so.6, libx264.so.160, etc.) under /usr/lib/compat to stay out of
the base system, and the image ships NO ld.so.cache. glibc's dynamic
linker therefore only searches the hardcoded /usr/lib + /lib plus
LD_LIBRARY_PATH, so /usr/lib/compat is invisible to anything that does
not explicitly prepend it. control.txt does set
LD_LIBRARY_PATH=/usr/lib/compat, but the per-runtime invocation
overwrites it, dropping compat for binaries like love.aarch64.

docs/PortMaster_CFW.md is explicit on the fix: "add these older .so
symlinks alongside [the newer versions]" and "Keep [libtheoradec] at
standard versions." So we expose the compat SONAMEs in the default path.

New archr-compat-symlinks script + archr-compat-libs.service oneshot
(ordered Before=archr-autostart so it runs before any port launches):
walks /usr/lib/compat (and /usr/lib32/compat) and symlinks each SONAME
into /usr/lib, but ONLY when the base system does not already provide
that exact name, so real libraries (libogg.so.0, libvorbis.so.0, ...)
are never shadowed. Idempotent via the `[ -e ]` guard; the writable
ext4 rootfs keeps the links so later boots are no-ops.

Verified live on the R36S: 46 compat-only SONAMEs linked into /usr/lib,
libtheoradec.so.1 now resolvable, libogg.so.0 left untouched (still the
real /usr/lib/libogg.so.0.8.6).
This commit is contained in:
Douglas Teles
2026-06-24 20:28:04 -03:00
parent a3d131f0d5
commit 1175e2b5a9
3 changed files with 66 additions and 0 deletions
+6
View File
@@ -153,6 +153,12 @@ EOF
### Take a backup of the system configuration on shutdown
enable_service save-sysconfig.service
### Expose PortMaster compat libs (/usr/lib/compat) in the default
### linker path. No ld.so.cache ships, so love_11.5 et al. fail on
### libtheoradec.so.1 unless it is symlinked into /usr/lib (see
### docs/PortMaster_CFW.md and the archr-compat-symlinks script).
enable_service archr-compat-libs.service
### System locale. glibc/package.mk only generates en_US.UTF-8; without
### this file LANG stays empty and every shell falls back to POSIX,
### which breaks UTF-8 input, pacman messages and emulator menus.
@@ -0,0 +1,45 @@
#!/bin/bash
# SPDX-License-Identifier: GPL-2.0
#
# archr-compat-symlinks: expose the PortMaster compat libraries
# (/usr/lib/compat/*.so*, the older Debian-11-era SONAMEs that ports and
# their runtimes link against) in the default linker search path.
#
# ArchR keeps these libs under /usr/lib/compat to stay out of the base
# system, but the image ships no ld.so.cache: the dynamic linker only
# searches the hardcoded /usr/lib + /lib plus LD_LIBRARY_PATH, so
# /usr/lib/compat is invisible to anything that does not explicitly
# prepend it. PortMaster runtimes such as love_11.5 link
# libtheoradec.so.1 as a normal system library and die with
# "cannot open shared object file: libtheoradec.so.1" when it lives only
# under compat. docs/PortMaster_CFW.md is explicit: "add these older .so
# symlinks alongside [the newer versions]" and "Keep [libtheoradec] at
# standard versions."
#
# We therefore symlink each compat lib into /usr/lib, but ONLY when the
# base system does not already provide that exact SONAME, so we never
# shadow a real system library (e.g. libogg.so.0, libvorbis.so.0).
#
# Idempotent: the `[ -e ]` guard skips anything already present, so it is
# safe to run every boot. The rootfs is writable ext4, so the links
# persist and later boots are no-ops.
link_dir() {
local compat="$1" libdir="$2" n=0 f base
[ -d "${compat}" ] || return 0
for f in "${compat}"/*.so*; do
[ -e "${f}" ] || continue
base="${f##*/}"
if [ ! -e "${libdir}/${base}" ]; then
ln -sf "compat/${base}" "${libdir}/${base}" && n=$((n + 1))
fi
done
echo "${n}"
}
created=$(link_dir /usr/lib/compat /usr/lib)
# armhf / 32-bit ports use the lib32 tree when present.
created32=$(link_dir /usr/lib32/compat /usr/lib32)
echo "archr-compat-symlinks: linked ${created:-0} (lib) + ${created32:-0} (lib32) compat SONAMEs into the default path"
exit 0
@@ -0,0 +1,15 @@
[Unit]
Description=ArchR PortMaster compat library symlinks
# Ports are launched from EmulationStation, which archr-autostart.service
# brings up; ordering before it guarantees the compat SONAMEs are linked
# into /usr/lib before any port (or its runtime) can dlopen them.
Before=archr-autostart.service
After=systemd-tmpfiles-setup.service
[Service]
Type=oneshot
ExecStart=/usr/bin/archr-compat-symlinks
RemainAfterExit=yes
[Install]
WantedBy=archr.target