1*4882a593Smuzhiyun#!/usr/bin/env bash 2*4882a593Smuzhiyun 3*4882a593Smuzhiyun# Copyright (C) 2016 Samuel Martin <s.martin49@gmail.com> 4*4882a593Smuzhiyun# Copyright (C) 2017 Wolfgang Grandegger <wg@grandegger.com> 5*4882a593Smuzhiyun# 6*4882a593Smuzhiyun# This program is free software; you can redistribute it and/or modify 7*4882a593Smuzhiyun# it under the terms of the GNU General Public License as published by 8*4882a593Smuzhiyun# the Free Software Foundation; either version 2 of the License, or 9*4882a593Smuzhiyun# (at your option) any later version. 10*4882a593Smuzhiyun# 11*4882a593Smuzhiyun# This program is distributed in the hope that it will be useful, 12*4882a593Smuzhiyun# but WITHOUT ANY WARRANTY; without even the implied warranty of 13*4882a593Smuzhiyun# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14*4882a593Smuzhiyun# General Public License for more details. 15*4882a593Smuzhiyun# 16*4882a593Smuzhiyun# You should have received a copy of the GNU General Public License 17*4882a593Smuzhiyun# along with this program; if not, write to the Free Software 18*4882a593Smuzhiyun# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 19*4882a593Smuzhiyun 20*4882a593Smuzhiyunusage() { 21*4882a593Smuzhiyun cat <<EOF >&2 22*4882a593SmuzhiyunUsage: ${0} TREE_KIND 23*4882a593Smuzhiyun 24*4882a593SmuzhiyunDescription: 25*4882a593Smuzhiyun 26*4882a593Smuzhiyun This script scans a tree and sanitize ELF files' RPATH found in there. 27*4882a593Smuzhiyun 28*4882a593Smuzhiyun Sanitization behaves the same whatever the kind of the processed tree, 29*4882a593Smuzhiyun but the resulting RPATH differs. The rpath sanitization is done using 30*4882a593Smuzhiyun "patchelf --make-rpath-relative". 31*4882a593Smuzhiyun 32*4882a593SmuzhiyunArguments: 33*4882a593Smuzhiyun 34*4882a593Smuzhiyun TREE_KIND Kind of tree to be processed. 35*4882a593Smuzhiyun Allowed values: host, target, staging 36*4882a593Smuzhiyun 37*4882a593SmuzhiyunEnvironment: 38*4882a593Smuzhiyun 39*4882a593Smuzhiyun PATCHELF patchelf program to use 40*4882a593Smuzhiyun (default: HOST_DIR/bin/patchelf) 41*4882a593Smuzhiyun 42*4882a593Smuzhiyun HOST_DIR host directory 43*4882a593Smuzhiyun STAGING_DIR staging directory 44*4882a593Smuzhiyun TARGET_DIR target directory 45*4882a593Smuzhiyun 46*4882a593Smuzhiyun TOOLCHAIN_EXTERNAL_DOWNLOAD_INSTALL_DIR 47*4882a593Smuzhiyun (default HOST_DIR/opt/ext-toolchain) 48*4882a593Smuzhiyun 49*4882a593SmuzhiyunReturns: 0 if success or 1 in case of error 50*4882a593Smuzhiyun 51*4882a593SmuzhiyunEOF 52*4882a593Smuzhiyun} 53*4882a593Smuzhiyun 54*4882a593Smuzhiyun: ${PATCHELF:=${HOST_DIR}/bin/patchelf} 55*4882a593Smuzhiyun 56*4882a593Smuzhiyun# ELF files should not be in these sub-directories 57*4882a593SmuzhiyunHOST_EXCLUDEPATHS="/share/terminfo" 58*4882a593SmuzhiyunSTAGING_EXCLUDEPATHS="/usr/include /usr/share/terminfo" 59*4882a593SmuzhiyunTARGET_EXCLUDEPATHS="/lib/firmware" 60*4882a593Smuzhiyun 61*4882a593Smuzhiyunmain() { 62*4882a593Smuzhiyun local rootdir 63*4882a593Smuzhiyun local tree="${1}" 64*4882a593Smuzhiyun local find_args=( ) 65*4882a593Smuzhiyun local sanitize_extra_args=( ) 66*4882a593Smuzhiyun 67*4882a593Smuzhiyun if ! "${PATCHELF}" --version > /dev/null 2>&1; then 68*4882a593Smuzhiyun echo "Error: can't execute patchelf utility '${PATCHELF}'" 69*4882a593Smuzhiyun exit 1 70*4882a593Smuzhiyun fi 71*4882a593Smuzhiyun 72*4882a593Smuzhiyun case "${tree}" in 73*4882a593Smuzhiyun host) 74*4882a593Smuzhiyun rootdir="${HOST_DIR}" 75*4882a593Smuzhiyun 76*4882a593Smuzhiyun # do not process the sysroot (only contains target binaries) 77*4882a593Smuzhiyun find_args+=( "-path" "${STAGING_DIR}" "-prune" "-o" ) 78*4882a593Smuzhiyun 79*4882a593Smuzhiyun # do not process the external toolchain installation directory to 80*4882a593Smuzhiyun # avoid breaking it. 81*4882a593Smuzhiyun test "${TOOLCHAIN_EXTERNAL_DOWNLOAD_INSTALL_DIR}" != "" && \ 82*4882a593Smuzhiyun find_args+=( "-path" "${TOOLCHAIN_EXTERNAL_DOWNLOAD_INSTALL_DIR}" "-prune" "-o" ) 83*4882a593Smuzhiyun 84*4882a593Smuzhiyun for excludepath in ${HOST_EXCLUDEPATHS}; do 85*4882a593Smuzhiyun find_args+=( "-path" "${HOST_DIR}""${excludepath}" "-prune" "-o" ) 86*4882a593Smuzhiyun done 87*4882a593Smuzhiyun 88*4882a593Smuzhiyun # do not process the patchelf binary but a copy to work-around "file in use" 89*4882a593Smuzhiyun find_args+=( "-path" "${PATCHELF}" "-prune" "-o" ) 90*4882a593Smuzhiyun cp "${PATCHELF}" "${PATCHELF}.__to_be_patched" 91*4882a593Smuzhiyun 92*4882a593Smuzhiyun # we always want $ORIGIN-based rpaths to make it relocatable. 93*4882a593Smuzhiyun sanitize_extra_args+=( "--relative-to-file" ) 94*4882a593Smuzhiyun ;; 95*4882a593Smuzhiyun 96*4882a593Smuzhiyun staging) 97*4882a593Smuzhiyun rootdir="${STAGING_DIR}" 98*4882a593Smuzhiyun 99*4882a593Smuzhiyun # ELF files should not be in these sub-directories 100*4882a593Smuzhiyun for excludepath in ${STAGING_EXCLUDEPATHS}; do 101*4882a593Smuzhiyun find_args+=( "-path" "${STAGING_DIR}""${excludepath}" "-prune" "-o" ) 102*4882a593Smuzhiyun done 103*4882a593Smuzhiyun 104*4882a593Smuzhiyun # should be like for the target tree below 105*4882a593Smuzhiyun sanitize_extra_args+=( "--no-standard-lib-dirs" ) 106*4882a593Smuzhiyun ;; 107*4882a593Smuzhiyun 108*4882a593Smuzhiyun target) 109*4882a593Smuzhiyun rootdir="${TARGET_DIR}" 110*4882a593Smuzhiyun 111*4882a593Smuzhiyun for excludepath in ${TARGET_EXCLUDEPATHS}; do 112*4882a593Smuzhiyun find_args+=( "-path" "${TARGET_DIR}""${excludepath}" "-prune" "-o" ) 113*4882a593Smuzhiyun done 114*4882a593Smuzhiyun 115*4882a593Smuzhiyun # we don't want $ORIGIN-based rpaths but absolute paths without rootdir. 116*4882a593Smuzhiyun # we also want to remove rpaths pointing to /lib or /usr/lib. 117*4882a593Smuzhiyun sanitize_extra_args+=( "--no-standard-lib-dirs" ) 118*4882a593Smuzhiyun ;; 119*4882a593Smuzhiyun 120*4882a593Smuzhiyun *) 121*4882a593Smuzhiyun usage 122*4882a593Smuzhiyun exit 1 123*4882a593Smuzhiyun ;; 124*4882a593Smuzhiyun esac 125*4882a593Smuzhiyun 126*4882a593Smuzhiyun find_args+=( "-type" "f" "-print" ) 127*4882a593Smuzhiyun 128*4882a593Smuzhiyun while read file ; do 129*4882a593Smuzhiyun # check if it's an ELF file 130*4882a593Smuzhiyun rpath=$(${PATCHELF} --print-rpath "${file}" 2>&1) 131*4882a593Smuzhiyun if test $? -ne 0 ; then 132*4882a593Smuzhiyun continue 133*4882a593Smuzhiyun fi 134*4882a593Smuzhiyun 135*4882a593Smuzhiyun # make files writable if necessary 136*4882a593Smuzhiyun changed=$(chmod -c u+w "${file}") 137*4882a593Smuzhiyun 138*4882a593Smuzhiyun # With per-package directory support, most RPATH of host 139*4882a593Smuzhiyun # binaries will point to per-package directories. This won't 140*4882a593Smuzhiyun # work with the --make-rpath-relative ${rootdir} invocation as 141*4882a593Smuzhiyun # the per-package host directory is not within ${rootdir}. So, 142*4882a593Smuzhiyun # we rewrite all RPATHs pointing to per-package directories so 143*4882a593Smuzhiyun # that they point to the global host directry. 144*4882a593Smuzhiyun changed_rpath=$(echo ${rpath} | sed "s@${PER_PACKAGE_DIR}/[^/]\+/host@${HOST_DIR}@") 145*4882a593Smuzhiyun if test "${rpath}" != "${changed_rpath}" ; then 146*4882a593Smuzhiyun ${PATCHELF} --set-rpath ${changed_rpath} "${file}" 147*4882a593Smuzhiyun fi 148*4882a593Smuzhiyun 149*4882a593Smuzhiyun # call patchelf to sanitize the rpath 150*4882a593Smuzhiyun ${PATCHELF} --make-rpath-relative "${rootdir}" ${sanitize_extra_args[@]} "${file}" 151*4882a593Smuzhiyun # restore the original permission 152*4882a593Smuzhiyun test "${changed}" != "" && chmod u-w "${file}" 153*4882a593Smuzhiyun done < <(find "${rootdir}" ${find_args[@]}) 154*4882a593Smuzhiyun 155*4882a593Smuzhiyun # Restore patched patchelf utility 156*4882a593Smuzhiyun test "${tree}" = "host" && mv "${PATCHELF}.__to_be_patched" "${PATCHELF}" 157*4882a593Smuzhiyun 158*4882a593Smuzhiyun # ignore errors 159*4882a593Smuzhiyun return 0 160*4882a593Smuzhiyun} 161*4882a593Smuzhiyun 162*4882a593Smuzhiyunmain ${@} 163