1*4882a593Smuzhiyun#!/usr/bin/env bash 2*4882a593Smuzhiyun 3*4882a593Smuzhiyun# This script scans $(HOST_DIR)/{bin,sbin} for all ELF files, and checks 4*4882a593Smuzhiyun# they have an RPATH to $(HOST_DIR)/lib if they need libraries from 5*4882a593Smuzhiyun# there. 6*4882a593Smuzhiyun 7*4882a593Smuzhiyun# Override the user's locale so we are sure we can parse the output of 8*4882a593Smuzhiyun# readelf(1) and file(1) 9*4882a593Smuzhiyunexport LC_ALL=C 10*4882a593Smuzhiyun 11*4882a593Smuzhiyunmain() { 12*4882a593Smuzhiyun local pkg="${1}" 13*4882a593Smuzhiyun local hostdir="${2}" 14*4882a593Smuzhiyun local perpackagedir="${3}" 15*4882a593Smuzhiyun local file ret 16*4882a593Smuzhiyun 17*4882a593Smuzhiyun # Remove duplicate and trailing '/' for proper match 18*4882a593Smuzhiyun hostdir="$( sed -r -e 's:/+:/:g; s:/$::;' <<<"${hostdir}" )" 19*4882a593Smuzhiyun 20*4882a593Smuzhiyun ret=0 21*4882a593Smuzhiyun while read file; do 22*4882a593Smuzhiyun is_elf "${file}" || continue 23*4882a593Smuzhiyun elf_needs_rpath "${file}" "${hostdir}" || continue 24*4882a593Smuzhiyun check_elf_has_rpath "${file}" "${hostdir}" "${perpackagedir}" && continue 25*4882a593Smuzhiyun if [ ${ret} -eq 0 ]; then 26*4882a593Smuzhiyun ret=1 27*4882a593Smuzhiyun printf "***\n" 28*4882a593Smuzhiyun printf "*** ERROR: package %s installs executables without proper RPATH:\n" "${pkg}" 29*4882a593Smuzhiyun fi 30*4882a593Smuzhiyun printf "*** %s\n" "${file}" 31*4882a593Smuzhiyun done < <( find "${hostdir}"/{bin,sbin} -type f 2>/dev/null ) 32*4882a593Smuzhiyun 33*4882a593Smuzhiyun return ${ret} 34*4882a593Smuzhiyun} 35*4882a593Smuzhiyun 36*4882a593Smuzhiyunis_elf() { 37*4882a593Smuzhiyun local f="${1}" 38*4882a593Smuzhiyun 39*4882a593Smuzhiyun readelf -l "${f}" 2>/dev/null \ 40*4882a593Smuzhiyun |grep -E 'Requesting program interpreter:' >/dev/null 2>&1 41*4882a593Smuzhiyun} 42*4882a593Smuzhiyun 43*4882a593Smuzhiyun# This function tells whether a given ELF executable (first argument) 44*4882a593Smuzhiyun# needs a RPATH pointing to the host library directory or not. It 45*4882a593Smuzhiyun# needs such an RPATH if at least of the libraries used by the ELF 46*4882a593Smuzhiyun# executable is available in the host library directory. This function 47*4882a593Smuzhiyun# returns 0 when a RPATH is needed, 1 otherwise. 48*4882a593Smuzhiyun# 49*4882a593Smuzhiyun# With per-package directory support, ${hostdir} will point to the 50*4882a593Smuzhiyun# current package per-package host directory, and this is where this 51*4882a593Smuzhiyun# function will check if the libraries needed by the executable are 52*4882a593Smuzhiyun# located (or not). In practice, the ELF executable RPATH may point to 53*4882a593Smuzhiyun# another package per-package host directory, but that is fine because 54*4882a593Smuzhiyun# if such an executable is within the current package per-package host 55*4882a593Smuzhiyun# directory, its libraries will also have been copied into the current 56*4882a593Smuzhiyun# package per-package host directory. 57*4882a593Smuzhiyunelf_needs_rpath() { 58*4882a593Smuzhiyun local file="${1}" 59*4882a593Smuzhiyun local hostdir="${2}" 60*4882a593Smuzhiyun local lib 61*4882a593Smuzhiyun 62*4882a593Smuzhiyun while read lib; do 63*4882a593Smuzhiyun [ -e "${hostdir}/lib/${lib}" ] && return 0 64*4882a593Smuzhiyun done < <( readelf -d "${file}" \ 65*4882a593Smuzhiyun |sed -r -e '/^.* \(NEEDED\) .*Shared library: \[(.+)\]$/!d;' \ 66*4882a593Smuzhiyun -e 's//\1/;' \ 67*4882a593Smuzhiyun ) 68*4882a593Smuzhiyun 69*4882a593Smuzhiyun return 1 70*4882a593Smuzhiyun} 71*4882a593Smuzhiyun 72*4882a593Smuzhiyun# This function checks whether at least one of the RPATH of the given 73*4882a593Smuzhiyun# ELF executable (first argument) properly points to the host library 74*4882a593Smuzhiyun# directory (second argument), either through an absolute RPATH or a 75*4882a593Smuzhiyun# relative RPATH. In the context of per-package directory support, 76*4882a593Smuzhiyun# ${hostdir} (second argument) points to the current package host 77*4882a593Smuzhiyun# directory. However, it is perfectly valid for an ELF binary to have 78*4882a593Smuzhiyun# a RPATH pointing to another package per-package host directory, 79*4882a593Smuzhiyun# which is why such RPATH is also accepted (the per-package directory 80*4882a593Smuzhiyun# gets passed as third argument). Having a RPATH pointing to the host 81*4882a593Smuzhiyun# directory will make sure the ELF executable will find at runtime the 82*4882a593Smuzhiyun# shared libraries it depends on. This function returns 0 when a 83*4882a593Smuzhiyun# proper RPATH was found, or 1 otherwise. 84*4882a593Smuzhiyuncheck_elf_has_rpath() { 85*4882a593Smuzhiyun local file="${1}" 86*4882a593Smuzhiyun local hostdir="${2}" 87*4882a593Smuzhiyun local perpackagedir="${3}" 88*4882a593Smuzhiyun local rpath dir 89*4882a593Smuzhiyun 90*4882a593Smuzhiyun while read rpath; do 91*4882a593Smuzhiyun for dir in ${rpath//:/ }; do 92*4882a593Smuzhiyun # Remove duplicate and trailing '/' for proper match 93*4882a593Smuzhiyun dir="$( sed -r -e 's:/+:/:g; s:/$::;' <<<"${dir}" )" 94*4882a593Smuzhiyun [ "${dir}" = "${hostdir}/lib" ] && return 0 95*4882a593Smuzhiyun [ "${dir}" = "\$ORIGIN/../lib" ] && return 0 96*4882a593Smuzhiyun # This check is done even for builds where 97*4882a593Smuzhiyun # BR2_PER_PACKAGE_DIRECTORIES is disabled. In this case, 98*4882a593Smuzhiyun # PER_PACKAGE_DIR and therefore ${perpackagedir} points to 99*4882a593Smuzhiyun # a non-existent directory, and this check will always be 100*4882a593Smuzhiyun # false. 101*4882a593Smuzhiyun [[ ${dir} =~ ${perpackagedir}/[^/]+/host/lib ]] && return 0 102*4882a593Smuzhiyun done 103*4882a593Smuzhiyun done < <( readelf -d "${file}" \ 104*4882a593Smuzhiyun |sed -r -e '/.* \(R(UN)?PATH\) +Library r(un)?path: \[(.+)\]$/!d' \ 105*4882a593Smuzhiyun -e 's//\3/;' \ 106*4882a593Smuzhiyun ) 107*4882a593Smuzhiyun 108*4882a593Smuzhiyun return 1 109*4882a593Smuzhiyun} 110*4882a593Smuzhiyun 111*4882a593Smuzhiyunmain "${@}" 112