check-host-rpath 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111
  1. #!/usr/bin/env bash
  2. # This script scans $(HOST_DIR)/{bin,sbin} for all ELF files, and checks
  3. # they have an RPATH to $(HOST_DIR)/lib if they need libraries from
  4. # there.
  5. # Override the user's locale so we are sure we can parse the output of
  6. # readelf(1) and file(1)
  7. export LC_ALL=C
  8. main() {
  9. local pkg="${1}"
  10. local hostdir="${2}"
  11. local perpackagedir="${3}"
  12. local file ret
  13. # Remove duplicate and trailing '/' for proper match
  14. hostdir="$( sed -r -e 's:/+:/:g; s:/$::;' <<<"${hostdir}" )"
  15. ret=0
  16. while read file; do
  17. is_elf "${file}" || continue
  18. elf_needs_rpath "${file}" "${hostdir}" || continue
  19. check_elf_has_rpath "${file}" "${hostdir}" "${perpackagedir}" && continue
  20. if [ ${ret} -eq 0 ]; then
  21. ret=1
  22. printf "***\n"
  23. printf "*** ERROR: package %s installs executables without proper RPATH:\n" "${pkg}"
  24. fi
  25. printf "*** %s\n" "${file}"
  26. done < <( find "${hostdir}"/{bin,sbin} -type f 2>/dev/null )
  27. return ${ret}
  28. }
  29. is_elf() {
  30. local f="${1}"
  31. readelf -l "${f}" 2>/dev/null \
  32. |grep -E 'Requesting program interpreter:' >/dev/null 2>&1
  33. }
  34. # This function tells whether a given ELF executable (first argument)
  35. # needs a RPATH pointing to the host library directory or not. It
  36. # needs such an RPATH if at least of the libraries used by the ELF
  37. # executable is available in the host library directory. This function
  38. # returns 0 when a RPATH is needed, 1 otherwise.
  39. #
  40. # With per-package directory support, ${hostdir} will point to the
  41. # current package per-package host directory, and this is where this
  42. # function will check if the libraries needed by the executable are
  43. # located (or not). In practice, the ELF executable RPATH may point to
  44. # another package per-package host directory, but that is fine because
  45. # if such an executable is within the current package per-package host
  46. # directory, its libraries will also have been copied into the current
  47. # package per-package host directory.
  48. elf_needs_rpath() {
  49. local file="${1}"
  50. local hostdir="${2}"
  51. local lib
  52. while read lib; do
  53. [ -e "${hostdir}/lib/${lib}" ] && return 0
  54. done < <( readelf -d "${file}" \
  55. |sed -r -e '/^.* \(NEEDED\) .*Shared library: \[(.+)\]$/!d;' \
  56. -e 's//\1/;' \
  57. )
  58. return 1
  59. }
  60. # This function checks whether at least one of the RPATH of the given
  61. # ELF executable (first argument) properly points to the host library
  62. # directory (second argument), either through an absolute RPATH or a
  63. # relative RPATH. In the context of per-package directory support,
  64. # ${hostdir} (second argument) points to the current package host
  65. # directory. However, it is perfectly valid for an ELF binary to have
  66. # a RPATH pointing to another package per-package host directory,
  67. # which is why such RPATH is also accepted (the per-package directory
  68. # gets passed as third argument). Having a RPATH pointing to the host
  69. # directory will make sure the ELF executable will find at runtime the
  70. # shared libraries it depends on. This function returns 0 when a
  71. # proper RPATH was found, or 1 otherwise.
  72. check_elf_has_rpath() {
  73. local file="${1}"
  74. local hostdir="${2}"
  75. local perpackagedir="${3}"
  76. local rpath dir
  77. while read rpath; do
  78. for dir in ${rpath//:/ }; do
  79. # Remove duplicate and trailing '/' for proper match
  80. dir="$( sed -r -e 's:/+:/:g; s:/$::;' <<<"${dir}" )"
  81. [ "${dir}" = "${hostdir}/lib" ] && return 0
  82. [ "${dir}" = "\$ORIGIN/../lib" ] && return 0
  83. # This check is done even for builds where
  84. # BR2_PER_PACKAGE_DIRECTORIES is disabled. In this case,
  85. # PER_PACKAGE_DIR and therefore ${perpackagedir} points to
  86. # a non-existent directory, and this check will always be
  87. # false.
  88. [[ ${dir} =~ ${perpackagedir}/[^/]+/host/lib ]] && return 0
  89. done
  90. done < <( readelf -d "${file}" \
  91. |sed -r -e '/.* \(R(UN)?PATH\) +Library r(un)?path: \[(.+)\]$/!d' \
  92. -e 's//\3/;' \
  93. )
  94. return 1
  95. }
  96. main "${@}"