PageRenderTime 1835ms CodeModel.GetById 1ms RepoModel.GetById 1ms app.codeStats 0ms

/toolchain/scripts/xldd.in

https://gitlab.com/thanhnhat041/padavan-ng
Autoconf | 472 lines | 379 code | 53 blank | 40 comment | 59 complexity | b60eeef9f45eff14412b0e14859a4e5d MD5 | raw file
  1. #!@@CT_bash@@
  2. # NON-CONFIGURABLE STUFF!
  3. export LC_ALL=C
  4. version="@@CT_VERSION@@"
  5. bits="@@CT_BITS@@"
  6. sed="${SED:-@@CT_sed@@}"
  7. grep="${GREP:-@@CT_grep@@}"
  8. my_name="$( basename "${0}" )"
  9. prefix="${0%-ldd}"
  10. gcc="${prefix}-gcc"
  11. readelf="${prefix}-readelf"
  12. fake_load_addr_root="$((0xdeadbeef))"
  13. fake_load_addr_rpath="$((0xdeadc0de))"
  14. fake_load_addr_sysroot="$((0x8badf00d))"
  15. ld_library_path="/lib:/usr/lib"
  16. need_e_class=
  17. need_e_flags=
  18. need_e_mach=
  19. do_error() {
  20. printf "%s: %s\n" "${my_name}" "$*" >&2
  21. }
  22. do_opt_error() {
  23. do_error "$@"
  24. printf "Try \`%s --help' for more information\n" "${my_name}" >&2
  25. }
  26. do_trace() {
  27. local depth=0
  28. [ -z "${CT_XLDD_VERBOSE}" ] && return 0
  29. for((depth=0; "${#FUNCNAME[$((depth+1))]}" != 0; depth++)); do :; done
  30. printf "%*s" $((4*(depth-1))) "" >&2
  31. printf -- "$@" >&2
  32. }
  33. show_version() {
  34. # Fake a real ldd, just in case some dumb script would check
  35. cat <<_EOF_
  36. ldd (crosstool-NG) ${version}
  37. Copyright (C) 2010 "Yann E. MORIN" <yann.morin.1998@free.fr>
  38. This is free software; see the source for copying conditions. There is NO
  39. warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  40. Licensed under the GPLv2, see the file LICENSE in the top-directory of the
  41. sources for this package.
  42. _EOF_
  43. }
  44. show_help() {
  45. cat <<_EOF_
  46. Usage: ${my_name} [OPTION]... --root DIR FILE...
  47. --help print this help and exit
  48. --version print version information and exit
  49. --root dir treat dir as being the root of the target
  50. -s, --show-system mark libs from the sysroot with a trailing '[*]'
  51. and libs found via RPATH with a trailing '[+]'
  52. _EOF_
  53. cat <<_EOF_ |fmt
  54. ${my_name} tries to mimick the behavior of a real native ldd, but can be
  55. used in a cross-development environment. Here is how it differs from a
  56. real native ldd:
  57. If the CT_XLDD_VERBOSE variable is set and non-empty, then ${my_name} will
  58. print a lot of debug messages, explaining how it builds the library
  59. search path, and how each library was found and why.
  60. The LD_LIBRARY_PATH variable is not used, as it can not reliably be
  61. guessed except at runtime, and we can't run.
  62. ${my_name} does not scan /etc/ld.so.cache, but instead uses /etc/ld.so.conf
  63. (it understands the include directives therein for libces that have that).
  64. ${my_name} also interprets (tries to!) the RPATH/RUNPATH records found in
  65. the dynamic ELF section. Such paths are searched for only relative to
  66. the specified root, not from the sysroot (see below). Also, those paths
  67. are searched for not only for the file they appear in, but also for its
  68. dependencies.
  69. ${my_name} will search the directory specified with --root for libraries
  70. to resolve the NEEDED tags. If --root is not set, then ${my_name} will
  71. use the value in the environment variable \${CT_XLDD_ROOT}. If neither
  72. is set, then this is an error.
  73. If NEEDED libraries can't be found in the specified root directory, then
  74. ${my_name} will also look in the sysroot of the toolchain to see if it
  75. can find them.
  76. For NEEDED libraries that were found, the output will look like:
  77. libneeded.so => /path/to/libneeded.so (0xloadaddr)
  78. and for those that were not found, the output will look like:
  79. libneeded.so not found
  80. The paths are relative to the specified root directory, or to the sysroot
  81. (eg. /lib/libneeded.so, /usr/lib/libneeded.so, and so on...).
  82. The expected load address 'loadaddr' is a faked address to match the output
  83. of the real ldd, but has no actual meaning (set to some constants for now,
  84. 0x8badf00d for libraries from the sysroot, 0xdeadc0de for those found via
  85. the RPATH/RUNPATH records, and 0xdeadbeef for others).
  86. _EOF_
  87. # Unimplemeted yet:
  88. # -d, --data-relocs process data relocations
  89. # -r, --function-relocs process data and function relocations
  90. # -u, --unused print unused direct dependencies
  91. # -v, --verbose print all information
  92. # See also this thread:
  93. # http://sourceware.org/ml/crossgcc/2008-09/msg00057.html
  94. }
  95. # Parse command line options
  96. root="${CT_XLDD_ROOT}"
  97. show_system=
  98. while true; do
  99. case "${1}" in
  100. --help)
  101. show_help
  102. exit 0
  103. ;;
  104. --version)
  105. show_version
  106. exit 0
  107. ;;
  108. --root)
  109. root="$2"
  110. shift
  111. ;;
  112. --root=*)
  113. root="${1#--root=}"
  114. ;;
  115. --show-system|-s)
  116. show_system=1
  117. ;;
  118. -*)
  119. do_opt_error "unrecognized option \`${1}'"
  120. exit 1
  121. ;;
  122. *)
  123. break
  124. ;;
  125. esac
  126. shift
  127. done
  128. # Sanity checks
  129. if [ -z "${root}" ]; then
  130. do_opt_error "no root given"
  131. exit 1
  132. fi
  133. if [ ! -d "${root}" ]; then
  134. do_error "\`${root}': no such file or directory"
  135. exit 1
  136. fi
  137. sysroot="$( "${gcc}" -print-sysroot 2>/dev/null )"
  138. if [ -z "${sysroot}" ]; then
  139. sysroot="$( "${gcc}" -print-file-name=libc.so 2>/dev/null \
  140. |${sed} -r -e 's:/usr/lib/libc.so$::;' \
  141. )"
  142. fi
  143. if [ -z "${sysroot}" ]; then
  144. do_error "unable to find sysroot for \`${gcc}'"
  145. fi
  146. do_report_needed_found() {
  147. local needed="${1}"
  148. local path="${2}"
  149. local origin="${3}"
  150. local loadaddr
  151. local sys
  152. case "${origin}" in
  153. root)
  154. loadaddr="${fake_load_addr_root}"
  155. ;;
  156. rpath)
  157. loadaddr="${fake_load_addr_rpath}"
  158. if [ -n "${show_system}" ]; then
  159. sys=" [+]"
  160. fi
  161. ;;
  162. sysroot)
  163. loadaddr="${fake_load_addr_sysroot}"
  164. if [ -n "${show_system}" ]; then
  165. sys=" [*]"
  166. fi
  167. ;;
  168. esac
  169. printf "%8s%s => %s (0x%0*x)%s\n" \
  170. "" \
  171. "${needed}" \
  172. "${path}" \
  173. "$((bits/4))" \
  174. "${loadaddr}" \
  175. "${sys}"
  176. }
  177. # Helper: passed flags and needed flags must either both have
  178. # a given flag set, or neither must have it.
  179. flag_match() {
  180. local flags="${1}"
  181. local e_flags="${2}"
  182. local dso="${3}"
  183. local f
  184. for f in ${flags}; do
  185. case "${e_flags}:${need_e_flags}" in
  186. *,${f},*:*,${f},*)
  187. # Both have it, continue
  188. ;;
  189. *,${f},*)
  190. # Only one has it
  191. do_trace "-> skip incompatible '%s' (flags '%s', need '%s')" \
  192. "${dso}" "${e_flags}" "${need_e_flags}"
  193. return 1;;
  194. *) ;; # Neither one has it, continue
  195. esac
  196. done
  197. return 0
  198. }
  199. __warned_unknown_machine=no
  200. do_check_compat() {
  201. local file="${1}"
  202. local info e_mach e_class e_flags
  203. local dso
  204. local -a flags
  205. if [ ! -r "${file}" ]; then
  206. return 1
  207. fi
  208. info=`${readelf} -Wh "${file}"`
  209. e_class=`echo "${info}" | "${sed}" -nr 's/.*Class:[[:space:]]*//p'`
  210. e_mach=`echo "${info}" | "${sed}" -nr 's/.*Machine:[[:space:]]*//p'`
  211. e_flags=`echo "${info}" | "${sed}" -nr -e 's/$/,/' -e 's/, /,/g' -e 's/.*Flags:[[:space:]]*0x[0-9a-f]{1,}//p'`
  212. dso="${base}${d}/${needed}"
  213. if [ "${e_class}" != "${need_e_class}" ]; then
  214. do_trace "-> skip incompatible '%s' (class '%s', need '%s')\n" \
  215. "${dso}" "${e_class}" "${need_e_class}"
  216. return 1
  217. fi
  218. if [ "${e_mach}" != "${need_e_mach}" ]; then
  219. do_trace "-> skip incompatible '%s' (machine '%s', need '%s')\n" \
  220. "${dso}" "${e_mach}" "${need_e_mach}"
  221. return 1
  222. fi
  223. # ElfXX_Ehdr.e_flags is trickier, the rules for compatibility are
  224. # different for different architectures (machines). The logic is
  225. # the same as in elf_machine_matches_host() in GNU libc.
  226. case "${e_mach}" in
  227. AArch64|"Advanced Micro Devices X86-64"|Alpha|ARM|"Intel 80386"|\
  228. MC68000|"Xilinx MicroBlaze"|"Altera Nios II"|"Renesas / SuperH SH")
  229. # Simple cases: flags are not taken into the account
  230. ;;
  231. "MIPS R3000")
  232. # glibc just rejects "fp64", says it is unsupported on linux.
  233. # but who knows, maybe one day ct-ng will be targeting non-linux too.
  234. if ! flag_match "abi2 nan2008 fp64" "${e_flags}" "${dso}"; then
  235. return 1
  236. fi
  237. ;;
  238. PowerPC)
  239. # For 32-bit PowerPC, flags not checked. For 64-bit, check ABI version:
  240. # 0 means "not using any features affected by the differences"; v1 and v2
  241. # are the defined ABI versions. Here we have a different check than glibc:
  242. # in glibc, the dynamic linker checks the ABI compatibility against its own
  243. # ABI while we here check against the binary ABI. Doing it properly requires
  244. # a rework of this script, and the current check seems to work even with a
  245. # trivial "hello world" (it is reported as "abiv1", so apparently no
  246. # applications report 0 as ABI version).
  247. if [ "${e_class}" = "ELF64" ] && ! flag_match "abiv1 abiv2" "${e_flags}" "${dso}"; then
  248. return 1
  249. fi
  250. ;;
  251. "IBM S/390")
  252. # Dynamic linker checks against the kernel capability for "highgprs" flag
  253. # at runtime, but we cannot do that. Report success.
  254. ;;
  255. Sparc|"Sparc v8+"|"Sparc v9")
  256. # According to glibc sources, even DSOs reporting these different e_machine
  257. # values may be compatible. We currently don't handle that, but might need to.
  258. ;;
  259. *)
  260. # Warn once on an unhandled architecture and assume flags are compatible
  261. if [ "${__warned_unknown_machine}" = "no" ]; then
  262. __warned_unknown_machine=yes
  263. do_error "Architecture ${e_mach} not handled by cross-ldd script; assuming match"
  264. fi
  265. ;;
  266. esac
  267. return 0
  268. }
  269. # Search a needed file, scanning ${lib_dir} in the root directory
  270. do_find_needed() {
  271. local needed="${1}"
  272. local -a list
  273. local -a dirs
  274. local found
  275. local where
  276. local base
  277. local d i
  278. do_trace "Searching for '%s'\n" "${needed}"
  279. # rpath shall come first!
  280. list=( \
  281. "rpath:${root}" \
  282. "root:${root}" \
  283. "sysroot:${sysroot}" \
  284. )
  285. for i in "${list[@]}"; do
  286. where="${i%%:*}"
  287. base="${i#*:}"
  288. if [ "${where}" = "rpath" ]; then
  289. dirs=( "${search_rpath[@]}" )
  290. else
  291. dirs=( "${needed_search_path[@]}" )
  292. fi
  293. for d in "${dirs[@]}"; do
  294. do_trace "-> looking in '%s' (%s)\n" "${d}" "${where}"
  295. if do_check_compat "${base}${d}/${needed}"; then
  296. found="${d}/${needed}"
  297. do_trace "---> found\n"
  298. break 2
  299. fi
  300. done
  301. done
  302. if [ -n "${found}" ]; then
  303. do_report_needed_found "${needed}" "${found}" "${where}"
  304. do_process_file "${base}${found}"
  305. else
  306. printf "%8s%s not found\n" "" "${needed}"
  307. fi
  308. do_trace "Done searching for '%s'\n" "${needed}"
  309. }
  310. # Scan a file for all NEEDED tags
  311. do_process_file() {
  312. local file="${1}"
  313. local initial="${2}"
  314. local -a save_search_rpath
  315. local n m
  316. local found
  317. local info
  318. do_trace "Parsing file '%s'\n" "${file}"
  319. save_search_rpath=( "${search_rpath[@]}" )
  320. for n in $( "${readelf}" -d "${file}" \
  321. |"${grep}" -E '\((RPATH|RUNPATH)\)' \
  322. |"${sed}" -r -e 's/^.*Library r(|un)path:[[:space:]]+\[(.*)\]$/\2/;'\
  323. ); do
  324. do_trace "-> adding rpath '%s'\n" "${n}"
  325. search_rpath+=( "${n}" )
  326. done
  327. do_trace ": search path:\n"
  328. for n in "${search_rpath[@]}" "${needed_search_path[@]}"; do
  329. do_trace ": - '%s'\n" "${n}"
  330. done
  331. do_trace ": end search path\n"
  332. if [ -n "${initial}" ]; then
  333. if ! "${readelf}" -Wl "${file}" |
  334. "${grep}" 'Requesting program interpreter: ' >/dev/null; then
  335. printf " not a dynamic executable\n"
  336. exit 1
  337. fi
  338. info=`${readelf} -Wh "${file}"`
  339. need_e_class=`echo "${info}" | "${sed}" -nr 's/.*Class:[[:space:]]*//p'`
  340. need_e_mach=`echo "${info}" | "${sed}" -nr 's/.*Machine:[[:space:]]*//p'`
  341. need_e_flags=`echo "${info}" | "${sed}" -nr -e 's/$/,/' -e 's/, /,/g' -e 's/.*Flags:[[:space:]]*0x[0-9a-f]{1,}//p'`
  342. fi
  343. for n in $( "${readelf}" -d "${file}" \
  344. |"${grep}" -E '\(NEEDED\)' \
  345. |"${sed}" -r -e 's/^.*Shared library:[[:space:]]+\[([^]]+)\].*/\1/;' \
  346. ); do
  347. found=0
  348. for m in "${needed_list[@]}"; do
  349. [ "${n}" = "${m}" ] && found=1 && break
  350. done
  351. if [ ${found} -ne 0 ]; then
  352. do_trace "-> skipping already known dependency '%s'\n" "${n}"
  353. continue
  354. fi
  355. do_trace "-> handling new dependency '%s'\n" "${n}"
  356. needed_list+=( "${n}" )
  357. do_find_needed "${n}"
  358. do_trace "-> done handling dependency '%s'\n" "${n}"
  359. done
  360. search_rpath=( "${save_search_rpath[@]}" )
  361. do_trace "Finished parsing file '%s'\n" "${file}"
  362. }
  363. # Recursively scan a /etc/ld.so.conf file
  364. do_scan_etc_ldsoconf() {
  365. local ldsoconf="${1}"
  366. local g
  367. local f
  368. [ -f "${ldsoconf}" ] || return 0
  369. do_trace "Parsing ld.so.conf: '%s'\n" "${ldsoconf}"
  370. while read line; do
  371. case "${line}" in
  372. include\ *)
  373. g="${root}${line#include }"
  374. do_trace "-> handling include directive '%s'\n" "${g}"
  375. for f in ${g}; do
  376. do_scan_etc_ldsoconf "${f}"
  377. done
  378. do_trace "-> finished handling include directive '%s'\n" "${g}"
  379. ;;
  380. \#*|"")
  381. ;;
  382. *)
  383. do_trace "-> adding search dir '%s'\n" "${line}"
  384. needed_search_path+=( "${line}" )
  385. ;;
  386. esac
  387. done <"${ldsoconf}"
  388. do_trace "Finished parsing ld.so.conf: '%s'\n" "${ldsoconf}"
  389. }
  390. if [ -z "${1}" ]; then
  391. show_help
  392. elif [ ! -r "${1}" ]; then
  393. do_error "${1}: No such file or directory"
  394. fi
  395. # Build up the full list of search directories
  396. declare -a needed_search_path
  397. do_trace "Adding basic lib dirs\n"
  398. ld_library_path="${ld_library_path}:"
  399. while [ -n "${ld_library_path}" ]; do
  400. d="${ld_library_path%%:*}"
  401. if [ -n "${d}" ]; then
  402. do_trace "-> adding search dir '%s'\n" "${d}"
  403. needed_search_path+=( "${d}" )
  404. fi
  405. ld_library_path="${ld_library_path#*:}"
  406. done
  407. do_trace "Done adding basic lib dirs\n"
  408. do_trace "Scanning '/etc/ld.so.conf'\n"
  409. do_scan_etc_ldsoconf "${root}/etc/ld.so.conf"
  410. do_trace "Done scanning '/etc/ld.so.conf'\n"
  411. do_trace "Search path:\n"
  412. for p in "${needed_search_path[@]}"; do
  413. do_trace "-> '%s'\n" "${p}"
  414. done
  415. declare -a needed_list
  416. declare -a search_rpath
  417. do_trace "Scanning file '%s'\n" "${1}"
  418. do_process_file "${1}" initial
  419. do_trace "Done scanning file '%s'\n" "${1}"