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