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 49 PARALLEL_JOBS number of parallel jobs to run 50 51Returns: 0 if success or 1 in case of error 52 53EOF 54} 55 56: "${PATCHELF:=${HOST_DIR}/bin/patchelf}" 57 58# ELF files should not be in these sub-directories 59HOST_EXCLUDEPATHS="/share/terminfo" 60STAGING_EXCLUDEPATHS="/usr/include /usr/share/terminfo" 61TARGET_EXCLUDEPATHS="/lib/firmware" 62 63patch_file() { 64 local PATCHELF rootdir file 65 local -a sanitize_extra_args 66 67 PATCHELF="${1}" 68 rootdir="${2}" 69 file="${3}" 70 shift 3 71 sanitize_extra_args=("${@}") 72 73 # check if it's an ELF file 74 rpath="$("${PATCHELF}" --print-rpath "${file}" 2>&1)" 75 if test $? -ne 0 ; then 76 return 0 77 fi 78 79 # make files writable if necessary 80 changed="$(chmod -c u+w "${file}")" 81 82 # With per-package directory support, most RPATH of host 83 # binaries will point to per-package directories. This won't 84 # work with the --make-rpath-relative ${rootdir} invocation as 85 # the per-package host directory is not within ${rootdir}. So, 86 # we rewrite all RPATHs pointing to per-package directories so 87 # that they point to the global host directry. 88 # shellcheck disable=SC2001 # ${var//search/replace} hard when search or replace have / in them 89 changed_rpath="$(echo "${rpath}" | sed "s@${PER_PACKAGE_DIR}/[^/]\+/host@${HOST_DIR}@")" 90 if test "${rpath}" != "${changed_rpath}" ; then 91 "${PATCHELF}" --set-rpath "${changed_rpath}" "${file}" 92 fi 93 94 # call patchelf to sanitize the rpath 95 "${PATCHELF}" --make-rpath-relative "${rootdir}" "${sanitize_extra_args[@]}" "${file}" 96 # restore the original permission 97 test "${changed}" != "" && chmod u-w "${file}" 98} 99 100main() { 101 local rootdir tree 102 local -a find_args sanitize_extra_args 103 104 tree="${1}" 105 106 if ! "${PATCHELF}" --version > /dev/null 2>&1; then 107 echo "Error: can't execute patchelf utility '${PATCHELF}'" 108 exit 1 109 fi 110 111 case "${tree}" in 112 host) 113 rootdir="${HOST_DIR}" 114 115 # do not process the sysroot (only contains target binaries) 116 find_args+=( "-path" "${STAGING_DIR}" "-prune" "-o" ) 117 118 # do not process the external toolchain installation directory to 119 # avoid breaking it. 120 test "${TOOLCHAIN_EXTERNAL_DOWNLOAD_INSTALL_DIR}" != "" && \ 121 find_args+=( "-path" "${TOOLCHAIN_EXTERNAL_DOWNLOAD_INSTALL_DIR}" "-prune" "-o" ) 122 123 for excludepath in ${HOST_EXCLUDEPATHS}; do 124 find_args+=( "-path" "${HOST_DIR}""${excludepath}" "-prune" "-o" ) 125 done 126 127 # do not process the patchelf binary but a copy to work-around "file in use" 128 find_args+=( "-path" "${PATCHELF}" "-prune" "-o" ) 129 cp "${PATCHELF}" "${PATCHELF}.__to_be_patched" 130 131 # we always want $ORIGIN-based rpaths to make it relocatable. 132 sanitize_extra_args+=( "--relative-to-file" ) 133 ;; 134 135 staging) 136 rootdir="${STAGING_DIR}" 137 138 # ELF files should not be in these sub-directories 139 for excludepath in ${STAGING_EXCLUDEPATHS}; do 140 find_args+=( "-path" "${STAGING_DIR}""${excludepath}" "-prune" "-o" ) 141 done 142 143 # should be like for the target tree below 144 sanitize_extra_args+=( "--no-standard-lib-dirs" ) 145 ;; 146 147 target) 148 rootdir="${TARGET_DIR}" 149 150 for excludepath in ${TARGET_EXCLUDEPATHS}; do 151 find_args+=( "-path" "${TARGET_DIR}""${excludepath}" "-prune" "-o" ) 152 done 153 154 # we don't want $ORIGIN-based rpaths but absolute paths without rootdir. 155 # we also want to remove rpaths pointing to /lib or /usr/lib. 156 sanitize_extra_args+=( "--no-standard-lib-dirs" ) 157 ;; 158 159 *) 160 usage 161 exit 1 162 ;; 163 esac 164 165 find_args+=( "-type" "f" "-print0" ) 166 167 export -f patch_file 168 # Limit the number of cores used 169 # shellcheck disable=SC2016 # ${@} has to be expanded in the sub-shell. 170 find "${rootdir}" "${find_args[@]}" \ 171 | xargs -0 -r -P "${PARALLEL_JOBS:-1}" -I {} \ 172 bash -c 'patch_file "${@}"' _ "${PATCHELF}" "${rootdir}" {} "${sanitize_extra_args[@]}" 173 174 # Restore patched patchelf utility 175 test "${tree}" = "host" && mv "${PATCHELF}.__to_be_patched" "${PATCHELF}" 176 177 # ignore errors 178 return 0 179} 180 181main "${@}" 182