xref: /OK3568_Linux_fs/buildroot/support/scripts/check-host-rpath (revision 4882a59341e53eb6f0b4789bf948001014eff981)
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