| #!/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 "${@}" |