2
1

fix-rpath 6.0 KB

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