diff --git a/Dockerfile b/Dockerfile index 8fad276..94c37f7 100644 --- a/Dockerfile +++ b/Dockerfile @@ -162,29 +162,14 @@ RUN set -xe && \ # 收集 glibc 运行时依赖(动态探测,避免固定版本) RUN set -xe && \ mkdir -p /runtime-libs && \ - ldd /src/subconverter /usr/lib/libmihomo.so | \ - awk '{for (i=1; i<=NF; i++) if ($i ~ "^/") print $i}' | \ - sort -u | \ - while read -r lib; do \ - if [ -e "$lib" ]; then \ - mkdir -p "/runtime-libs$(dirname "$lib")" && \ - cp -aL "$lib" "/runtime-libs$lib"; \ - fi; \ - done && \ - for loader in /lib64/ld-linux-x86-64.so.2 /lib/x86_64-linux-gnu/ld-linux-x86-64.so.2 /lib/ld-linux-aarch64.so.1 /lib/aarch64-linux-gnu/ld-linux-aarch64.so.1; do \ - if [ -e "$loader" ]; then \ - mkdir -p "/runtime-libs$(dirname "$loader")" && \ - cp -aL "$loader" "/runtime-libs$loader"; \ - fi; \ - done && \ - libc_path="$(ldd /src/subconverter | awk '$1 == "libc.so.6" {print $3; exit}')" && \ - libc_dir="$(dirname "${libc_path:-/lib/x86_64-linux-gnu/libc.so.6}")" && \ - for extra in libnss_dns.so.2 libnss_files.so.2 libnss_compat.so.2 libresolv.so.2; do \ - if [ -e "$libc_dir/$extra" ]; then \ - mkdir -p "/runtime-libs$libc_dir" && \ - cp -aL "$libc_dir/$extra" "/runtime-libs$libc_dir/$extra"; \ - fi; \ - done && \ + ELF_LIBRARY_PATH="/usr/lib:/lib/x86_64-linux-gnu:/usr/lib/x86_64-linux-gnu:/lib/aarch64-linux-gnu:/usr/lib/aarch64-linux-gnu:/lib64" \ + bash /src/scripts/ci/copy-elf-runtime-deps.sh /runtime-libs \ + /src/subconverter \ + /usr/lib/libmihomo.so \ + libnss_dns.so.2 \ + libnss_files.so.2 \ + libnss_compat.so.2 \ + libresolv.so.2 && \ if [ -f /etc/nsswitch.conf ]; then \ mkdir -p /runtime-libs/etc && \ cp -aL /etc/nsswitch.conf /runtime-libs/etc/nsswitch.conf; \ @@ -215,9 +200,7 @@ RUN apk add --no-cache ca-certificates tzdata && \ COPY --from=builder /src/subconverter /usr/bin/subconverter COPY --from=builder /src/base /base/ -COPY --from=builder /usr/lib/libmihomo.so /usr/lib/ COPY --from=builder /runtime-libs/ / -COPY --from=builder /etc/nsswitch.conf /etc/nsswitch.conf # 确保二进制和库可执行 RUN chmod +x /usr/bin/subconverter && chmod +x /usr/lib/libmihomo.so diff --git a/docker/Dockerfile.armv7-cross b/docker/Dockerfile.armv7-cross index 2206f56..927f6b8 100644 --- a/docker/Dockerfile.armv7-cross +++ b/docker/Dockerfile.armv7-cross @@ -184,15 +184,14 @@ RUN set -xe && \ ln -snf /usr/share/zoneinfo/Asia/Shanghai /runtime-root/etc/localtime && \ echo Asia/Shanghai > /runtime-root/etc/timezone && \ if [ -f /etc/nsswitch.conf ]; then cp -aL /etc/nsswitch.conf /runtime-root/etc/nsswitch.conf; fi && \ - for dir in /lib/arm-linux-gnueabihf /usr/lib/arm-linux-gnueabihf /usr/arm-linux-gnueabihf/lib; do \ - if [ -d "$dir" ]; then \ - find "$dir" -maxdepth 1 \( -name '*.so' -o -name '*.so.*' \) -print | \ - while read -r lib; do \ - dest="/runtime-root/usr/lib/arm-linux-gnueabihf/$(basename "$lib")"; \ - cp -aL "$lib" "$dest"; \ - done; \ - fi; \ - done + READELF=arm-linux-gnueabihf-readelf \ + ELF_LIBRARY_PATH="/usr/lib/arm-linux-gnueabihf:/lib/arm-linux-gnueabihf:/usr/arm-linux-gnueabihf/lib:/usr/lib:/lib" \ + bash /src/scripts/ci/copy-elf-runtime-deps.sh /runtime-root \ + /src/subconverter \ + libnss_dns.so.2 \ + libnss_files.so.2 \ + libnss_compat.so.2 \ + libresolv.so.2 # ========== FINAL STAGE ========== FROM --platform=$TARGETPLATFORM mirror.gcr.io/library/debian:trixie-slim diff --git a/scripts/ci/copy-elf-runtime-deps.sh b/scripts/ci/copy-elf-runtime-deps.sh new file mode 100644 index 0000000..e14d16f --- /dev/null +++ b/scripts/ci/copy-elf-runtime-deps.sh @@ -0,0 +1,126 @@ +#!/usr/bin/env bash +set -euo pipefail + +DEST_ROOT="${1:?destination root is required}" +shift + +READELF="${READELF:-readelf}" +ELF_LIBRARY_PATH="${ELF_LIBRARY_PATH:-}" + +declare -a SEARCH_DIRS=() +if [ -n "${ELF_LIBRARY_PATH}" ]; then + IFS=':' read -r -a SEARCH_DIRS <<< "${ELF_LIBRARY_PATH}" +fi + +SEARCH_DIRS+=( + /lib + /usr/lib + /lib64 + /usr/lib64 + /lib/x86_64-linux-gnu + /usr/lib/x86_64-linux-gnu + /lib/aarch64-linux-gnu + /usr/lib/aarch64-linux-gnu + /lib/arm-linux-gnueabihf + /usr/lib/arm-linux-gnueabihf + /usr/arm-linux-gnueabihf/lib +) + +declare -A COPIED=() +declare -A SCANNED=() +declare -a QUEUE=() + +canonical_path() { + local path="$1" + if command -v realpath >/dev/null 2>&1; then + realpath "$path" + else + readlink -f "$path" + fi +} + +copy_runtime_file() { + local source="$1" + local logical="$source" + local resolved + resolved="$(canonical_path "$source")" + + if [ -n "${COPIED[$logical]:-}" ]; then + return + fi + + local dest="${DEST_ROOT}${logical}" + mkdir -p "$(dirname "$dest")" + cp -aL "$source" "$dest" + COPIED["$logical"]=1 + QUEUE+=("$resolved") +} + +resolve_soname() { + local soname="$1" + + if [[ "$soname" == */* ]] && [ -e "$soname" ]; then + printf '%s\n' "$soname" + return 0 + fi + + local dir candidate + for dir in "${SEARCH_DIRS[@]}"; do + [ -n "$dir" ] || continue + candidate="${dir}/${soname}" + if [ -e "$candidate" ]; then + printf '%s\n' "$candidate" + return 0 + fi + done + + return 1 +} + +copy_needed_by() { + local elf="$1" + + if [ -n "${SCANNED[$elf]:-}" ]; then + return + fi + SCANNED["$elf"]=1 + + if ! "$READELF" -h "$elf" >/dev/null 2>&1; then + return + fi + + local interp + interp="$("$READELF" -l "$elf" 2>/dev/null | sed -n 's/.*Requesting program interpreter: \(.*\)\]/\1/p' | head -n 1)" + if [ -n "$interp" ] && [ -e "$interp" ]; then + copy_runtime_file "$interp" + fi + + local needed resolved + while IFS= read -r needed; do + [ -n "$needed" ] || continue + if resolved="$(resolve_soname "$needed")"; then + copy_runtime_file "$resolved" + else + echo "warning: could not resolve ELF dependency '$needed' required by $elf" >&2 + fi + done < <("$READELF" -d "$elf" 2>/dev/null | sed -n 's/.*Shared library: \[\(.*\)\].*/\1/p') +} + +for input in "$@"; do + if [ -e "$input" ]; then + case "$(basename "$input")" in + *.so|*.so.*|ld-*.so*|ld-linux*.so*) copy_runtime_file "$input" ;; + esac + copy_needed_by "$(canonical_path "$input")" + elif resolved="$(resolve_soname "$input")"; then + copy_runtime_file "$resolved" + else + echo "warning: could not resolve requested runtime file '$input'" >&2 + fi +done + +while [ "${#QUEUE[@]}" -gt 0 ]; do + current="${QUEUE[0]}" + QUEUE=("${QUEUE[@]:1}") + copy_needed_by "$current" +done diff --git a/scripts/ci/extract-builder-output.sh b/scripts/ci/extract-builder-output.sh index d7b6232..9de35a3 100644 --- a/scripts/ci/extract-builder-output.sh +++ b/scripts/ci/extract-builder-output.sh @@ -25,7 +25,6 @@ chmod +x ./subconverter case "${MODE}" in shared) docker cp "${CID}:/runtime-libs" ./runtime-libs - docker cp "${CID}:/usr/lib/libmihomo.so" ./libmihomo.so ;; root) docker cp "${CID}:/runtime-root" ./runtime-root diff --git a/scripts/package-linux-portable.sh b/scripts/package-linux-portable.sh index 22a40e2..3d5cb5c 100644 --- a/scripts/package-linux-portable.sh +++ b/scripts/package-linux-portable.sh @@ -27,11 +27,6 @@ mkdir -p "${PACKAGE_DIR}" install -m755 subconverter "${PACKAGE_DIR}/subconverter" cp -a base "${PACKAGE_DIR}/" -if [ -f libmihomo.so ]; then - mkdir -p "${PACKAGE_DIR}/usr/lib" - install -m755 libmihomo.so "${PACKAGE_DIR}/usr/lib/libmihomo.so" -fi - copy_dir_contents runtime-libs copy_dir_contents runtime-root