#!/usr/bin/env bash # This script scans $(HOST_DIR)/{bin,sbin} for all ELF files, and checks # they have an RPATH to $(HOST_DIR)/lib if they need libraries from # there. # Override the user's locale so we are sure we can parse the output of # readelf(1) and file(1) export LC_ALL=C main() { local pkg="${1}" local hostdir="${2}" local perpackagedir="${3}" local file ret # Remove duplicate and trailing '/' for proper match hostdir="$( sed -r -e 's:/+:/:g; s:/$::;' <<<"${hostdir}" )" ret=0 while read file; do is_elf "${file}" || continue elf_needs_rpath "${file}" "${hostdir}" || continue check_elf_has_rpath "${file}" "${hostdir}" "${perpackagedir}" && continue if [ ${ret} -eq 0 ]; then ret=1 printf "***\n" printf "*** ERROR: package %s installs executables without proper RPATH:\n" "${pkg}" fi printf "*** %s\n" "${file}" done < <( find "${hostdir}"/{bin,sbin} -type f 2>/dev/null ) return ${ret} } is_elf() { local f="${1}" readelf -l "${f}" 2>/dev/null \ |grep -E 'Requesting program interpreter:' >/dev/null 2>&1 } # This function tells whether a given ELF executable (first argument) # needs a RPATH pointing to the host library directory or not. It # needs such an RPATH if at least of the libraries used by the ELF # executable is available in the host library directory. This function # returns 0 when a RPATH is needed, 1 otherwise. # # With per-package directory support, ${hostdir} will point to the # current package per-package host directory, and this is where this # function will check if the libraries needed by the executable are # located (or not). In practice, the ELF executable RPATH may point to # another package per-package host directory, but that is fine because # if such an executable is within the current package per-package host # directory, its libraries will also have been copied into the current # package per-package host directory. elf_needs_rpath() { local file="${1}" local hostdir="${2}" local lib while read lib; do [ -e "${hostdir}/lib/${lib}" ] && return 0 done < <( readelf -d "${file}" \ |sed -r -e '/^.* \(NEEDED\) .*Shared library: \[(.+)\]$/!d;' \ -e 's//\1/;' \ ) return 1 } # This function checks whether at least one of the RPATH of the given # ELF executable (first argument) properly points to the host library # directory (second argument), either through an absolute RPATH or a # relative RPATH. In the context of per-package directory support, # ${hostdir} (second argument) points to the current package host # directory. However, it is perfectly valid for an ELF binary to have # a RPATH pointing to another package per-package host directory, # which is why such RPATH is also accepted (the per-package directory # gets passed as third argument). Having a RPATH pointing to the host # directory will make sure the ELF executable will find at runtime the # shared libraries it depends on. This function returns 0 when a # proper RPATH was found, or 1 otherwise. check_elf_has_rpath() { local file="${1}" local hostdir="${2}" local perpackagedir="${3}" local rpath dir while read rpath; do for dir in ${rpath//:/ }; do # Remove duplicate and trailing '/' for proper match dir="$( sed -r -e 's:/+:/:g; s:/$::;' <<<"${dir}" )" [ "${dir}" = "${hostdir}/lib" ] && return 0 [ "${dir}" = "\$ORIGIN/../lib" ] && return 0 # This check is done even for builds where # BR2_PER_PACKAGE_DIRECTORIES is disabled. In this case, # PER_PACKAGE_DIR and therefore ${perpackagedir} points to # a non-existent directory, and this check will always be # false. [[ ${dir} =~ ${perpackagedir}/[^/]+/host/lib ]] && return 0 done done < <( readelf -d "${file}" \ |sed -r -e '/.* \(R(UN)?PATH\) +Library r(un)?path: \[(.+)\]$/!d' \ -e 's//\3/;' \ ) return 1 } main "${@}"