yann@2034: #!@@CT_bash@@ yann@2034: yann@2034: # NON-CONFIGURABLE STUFF! yann@2034: export LC_ALL=C yann@2184: version="@@CT_VERSION@@" yann@2187: bits="@@CT_BITS@@" yann@2034: sed="@@CT_sed@@" yann@2034: grep="@@CT_grep@@" yann@2187: yann@2034: my_name="$( basename "${0}" )" yann@2034: prefix="${0%-ldd}" yann@2034: gcc="${prefix}-gcc" yann@2034: readelf="${prefix}-readelf" yann@2192: fake_load_addr_root="$((0xdeadbeef))" yann@2193: fake_load_addr_rpath="$((0xdeadc0de))" yann@2192: fake_load_addr_sysroot="$((0x8badf00d))" yann@2034: ld_library_path="/lib:/usr/lib" yann@2034: yann@2034: do_error() { yann@2034: printf "%s: %s\n" "${my_name}" "$*" >&2 yann@2034: } yann@2034: yann@2034: do_opt_error() { yann@2034: do_error "$@" yann@2549: printf "Try \`%s --help' for more information\n" "${my_name}" >&2 yann@2034: } yann@2034: yann@2190: do_trace() { yann@2190: local depth=0 yann@2190: yann@2190: [ -z "${CT_XLDD_VERBOSE}" ] && return 0 yann@2190: yann@2190: for((depth=0; "${#FUNCNAME[$((depth+1))]}" != 0; depth++)); do :; done yann@2190: printf "%*s" $((4*(depth-1))) "" >&2 yann@2190: printf -- "$@" >&2 yann@2190: } yann@2190: yann@2034: show_version() { yann@2034: # Fake a real ldd, just in case some dumb script would check yann@2034: cat <<_EOF_ yann@2184: ldd (crosstool-NG) ${version} yann@2034: Copyright (C) 2010 "Yann E. MORIN" yann@2034: This is free software; see the source for copying conditions. There is NO yann@2034: warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. yann@2034: Licensed under the GPLv2, see the file LICENSES in the top-directory of the yann@2034: sources for this package. yann@2034: _EOF_ yann@2034: } yann@2034: yann@2034: show_help() { yann@2034: cat <<_EOF_ yann@2034: Usage: ${my_name} [OPTION]... --root DIR FILE... yann@2034: --help print this help and exit yann@2034: --version print version information and exit yann@2034: --root dir treat dir as being the root of the target yann@2034: -s, --show-system mark libs from the sysroot with a trailing '[*]' yann@2193: and libs found via RPATH with a trailing '[+]' yann@2034: yann@2034: _EOF_ yann@2034: cat <<_EOF_ |fmt yann@2034: ${my_name} tries to mimick the behavior of a real native ldd, but can be yann@2034: used in a cross-development environment. Here is how it differs from a yann@2034: real native ldd: yann@2034: yann@2546: If the CT_XLDD_VERBOSE variable is set and non-empty, then ${my_name} will yann@2190: print a lot of debug messages, explaining how it builds the library yann@2190: search path, and how each library was found and why. yann@2190: yann@2034: The LD_LIBRARY_PATH variable is not used, as it can not reliably be yann@2034: guessed except at runtime, and we can't run. yann@2034: yann@2034: ${my_name} does not scan /etc/ld.so.cache, but instead uses /etc/ld.so.conf yann@2034: (it understands the include directives therein for libces that have that). yann@2034: yann@2193: ${my_name} also interprets (tries to!) the RPATH/RUNPATH records found in yann@2193: the dynamic ELF section. Such paths are searched for only relative to yann@2193: the specified root, not from the sysroot (see below). Also, those paths yann@2193: are searched for not only for the file they appear in, but also for its yann@2193: dependencies. yann@2193: yann@2034: ${my_name} will search the directory specified with --root for libraries yann@2034: to resolve the NEEDED tags. If --root is not set, then ${my_name} will yann@2034: use the value in the environment variable \${CT_XLDD_ROOT}. If neither yann@2034: is set, then this is an error. yann@2034: yann@2034: If NEEDED libraries can't be found in the specified root directory, then yann@2034: ${my_name} will also look in the sysroot of the toolchain to see if it yann@2034: can find them. yann@2034: yann@2034: For NEEDED libraries that were found, the output will look like: yann@2034: libneeded.so => /path/to/libneeded.so (0xloadaddr) yann@2034: yann@2034: and for those that were not found, the output will look like: yann@2034: libneeded.so not found yann@2034: yann@2034: The paths are relative to the specified root directory, or to the sysroot yann@2034: (eg. /lib/libneeded.so, /usr/lib/libneeded.so, and so on...). yann@2034: yann@2034: The expected load address 'loadaddr' is a faked address to match the output yann@2034: of the real ldd, but has no actual meaning (set to some constants for now, yann@2193: 0x8badf00d for libraries from the sysroot, 0xdeadc0de for those found via yann@2193: the RPATH/RUNPATH records, and 0xdeadbeef for others). yann@2034: _EOF_ yann@2034: yann@2034: # Unimplemeted yet: yann@2034: # -d, --data-relocs process data relocations yann@2034: # -r, --function-relocs process data and function relocations yann@2034: # -u, --unused print unused direct dependencies yann@2034: # -v, --verbose print all information yann@2034: yann@2034: # See also this thread: yann@2034: # http://sourceware.org/ml/crossgcc/2008-09/msg00057.html yann@2034: } yann@2034: yann@2034: # Parse command line options yann@2034: root="${CT_XLDD_ROOT}" yann@2034: show_system= yann@2034: while true; do yann@2034: case "${1}" in yann@2034: --help) yann@2034: show_help yann@2034: exit 0 yann@2034: ;; yann@2034: --version) yann@2034: show_version yann@2034: exit 0 yann@2034: ;; yann@2034: --root) yann@2034: root="$2" yann@2034: shift yann@2034: ;; yann@2034: --root=*) yann@2034: root="${1#--root=}" yann@2034: ;; yann@2034: --show-system|-s) yann@2034: show_system=1 yann@2034: ;; yann@2034: -*) yann@2034: do_opt_error "unrecognized option \`${1}'" yann@2034: exit 1 yann@2034: ;; yann@2034: *) yann@2034: break yann@2034: ;; yann@2034: esac yann@2034: shift yann@2034: done yann@2034: yann@2034: # Sanity checks yann@2034: if [ -z "${root}" ]; then yann@2034: do_opt_error "no root given" yann@2034: exit 1 yann@2034: fi yann@2034: if [ ! -d "${root}" ]; then yann@2034: do_error "\`${root}': no such file or directory" yann@2034: exit 1 yann@2034: fi yann@2034: yann@2188: sysroot="$( "${gcc}" -print-sysroot 2>/dev/null )" yann@2188: if [ -z "${sysroot}" ]; then yann@2188: sysroot="$( "${gcc}" -print-file-name=libc.so 2>/dev/null \ yann@2188: |sed -r -e 's:/usr/lib/libc.so$::;' \ yann@2188: )" yann@2188: fi yann@2188: if [ -z "${sysroot}" ]; then yann@2188: do_error "unable to find sysroot for \`${gcc}'" yann@2188: fi yann@2034: yann@2034: do_report_needed_found() { yann@2034: local needed="${1}" yann@2034: local path="${2}" yann@2192: local origin="${3}" yann@2034: local loadaddr yann@2034: local sys yann@2034: yann@2192: case "${origin}" in yann@2192: root) yann@2192: loadaddr="${fake_load_addr_root}" yann@2192: ;; yann@2193: rpath) yann@2193: loadaddr="${fake_load_addr_rpath}" yann@2193: if [ -n "${show_system}" ]; then yann@2193: sys=" [+]" yann@2193: fi yann@2193: ;; yann@2192: sysroot) yann@2192: loadaddr="${fake_load_addr_sysroot}" yann@2192: if [ -n "${show_system}" ]; then yann@2192: sys=" [*]" yann@2192: fi yann@2192: ;; yann@2192: esac yann@2034: yann@2034: printf "%8s%s => %s (0x%0*x)%s\n" \ yann@2034: "" \ yann@2034: "${needed}" \ yann@2034: "${path}" \ yann@2187: "$((bits/4))" \ yann@2034: "${loadaddr}" \ yann@2034: "${sys}" yann@2034: } yann@2034: yann@2034: # Search a needed file, scanning ${lib_dir} in the root directory yann@2034: do_find_needed() { yann@2034: local needed="${1}" yann@2192: local -a list yann@2193: local -a dirs yann@2034: local found yann@2192: local where yann@2192: local base yann@2192: local d i yann@2034: yann@2548: do_trace "Searching for '%s'\n" "${needed}" yann@2190: yann@2193: # rpath shall come first! yann@2192: list=( \ yann@2193: "rpath:${root}" \ yann@2192: "root:${root}" \ yann@2192: "sysroot:${sysroot}" \ yann@2192: ) yann@2192: yann@2192: for i in "${list[@]}"; do yann@2192: where="${i%%:*}" yann@2192: base="${i#*:}" yann@2193: if [ "${where}" = "rpath" ]; then yann@2193: dirs=( "${search_rpath[@]}" ) yann@2193: else yann@2193: dirs=( "${needed_search_path[@]}" ) yann@2193: fi yann@2193: for d in "${dirs[@]}"; do yann@2548: do_trace "-> looking in '%s' (%s)\n" "${d}" "${where}" yann@2192: if [ -f "${base}${d}/${needed}" ]; then yann@2192: found="${d}/${needed}" yann@2192: do_trace "---> found\n" yann@2192: break 2 yann@2192: fi yann@2192: done yann@2034: done yann@2034: yann@2034: if [ -n "${found}" ]; then yann@2192: do_report_needed_found "${needed}" "${found}" "${where}" yann@2192: do_process_file "${base}${found}" yann@2034: else yann@2192: printf "%8s%s not found\n" "" "${needed}" yann@2034: fi yann@2547: yann@2548: do_trace "Done searching for '%s'\n" "${needed}" yann@2034: } yann@2034: yann@2034: # Scan a file for all NEEDED tags yann@2034: do_process_file() { yann@2034: local file="${1}" yann@2193: local -a save_search_rpath yann@2191: local n m yann@2191: local found yann@2034: yann@2548: do_trace "Parsing file '%s'\n" "${file}" yann@2190: yann@2193: save_search_rpath=( "${search_rpath[@]}" ) yann@2193: for n in $( "${readelf}" -d "${file}" \ yann@2193: |"${grep}" -E '\((RPATH|RUNPATH)\)' \ yann@2193: |"${sed}" -r -e 's/^.*Library r(|un)path:[[:space:]]+\[(.*)\]$/\2/;'\ yann@2193: ); do yann@2548: do_trace "-> adding rpath '%s'\n" "${n}" yann@2193: search_rpath+=( "${n}" ) yann@2193: done yann@2193: do_trace ": search path:\n" yann@2193: for n in "${search_rpath[@]}" "${needed_search_path[@]}"; do yann@2548: do_trace ": - '%s'\n" "${n}" yann@2193: done yann@2547: do_trace ": end search path" yann@2193: yann@2191: for n in $( "${readelf}" -d "${file}" \ yann@2191: |"${grep}" -E '\(NEEDED\)' \ yann@2191: |"${sed}" -r -e 's/^.*Shared library:[[:space:]]+\[(.*)\]$/\1/;' \ yann@2191: ); do yann@2191: found=0 yann@2191: for m in "${needed_list[@]}"; do yann@2191: [ "${n}" = "${m}" ] && found=1 && break yann@2191: done yann@2191: if [ ${found} -ne 0 ]; then yann@2548: do_trace "-> skipping already known dependency '%s'\n" "${n}" yann@2191: continue yann@2191: fi yann@2548: do_trace "-> handling new dependency '%s'\n" "${n}" yann@2191: needed_list+=( "${n}" ) yann@2191: do_find_needed "${n}" yann@2548: do_trace "-> done handling dependency '%s'\n" "${n}" yann@2193: done yann@2193: yann@2193: search_rpath=( "${save_search_rpath[@]}" ) yann@2547: yann@2548: do_trace "Finished parsing file '%s'\n" "${file}" yann@2034: } yann@2034: yann@2189: # Recursively scan a /etc/ld.so.conf file yann@2189: do_scan_etc_ldsoconf() { yann@2189: local ldsoconf="${1}" yann@2189: local g yann@2189: local f yann@2189: yann@2189: [ -f "${ldsoconf}" ] || return 0 yann@2548: do_trace "Parsing ld.so.conf: '%s'\n" "${ldsoconf}" yann@2189: yann@2189: while read line; do yann@2189: case "${line}" in yann@2189: include\ *) yann@2189: g="${root}${line#include }" yann@2548: do_trace "-> handling include directive '%s'\n" "${g}" yann@2189: for f in ${g}; do yann@2189: do_scan_etc_ldsoconf "${f}" yann@2189: done yann@2548: do_trace "-> finished handling include directive '%s'\n" "${g}" yann@2189: ;; yann@2189: \#*|"") yann@2189: ;; yann@2189: *) yann@2548: do_trace "-> adding search dir '%s'\n" "${line}" yann@2189: needed_search_path+=( "${line}" ) yann@2189: ;; yann@2189: esac yann@2189: done <"${ldsoconf}" yann@2547: yann@2548: do_trace "Finished parsing ld.so.conf: '%s'\n" "${ldsoconf}" yann@2189: } yann@2189: yann@2034: # Build up the full list of search directories yann@2034: declare -a needed_search_path yann@2190: do_trace "Adding basic lib dirs\n" yann@2034: ld_library_path="${ld_library_path}:" yann@2034: while [ -n "${ld_library_path}" ]; do yann@2034: d="${ld_library_path%%:*}" yann@2190: if [ -n "${d}" ]; then yann@2548: do_trace "-> adding search dir '%s'\n" "${d}" yann@2190: needed_search_path+=( "${d}" ) yann@2190: fi yann@2034: ld_library_path="${ld_library_path#*:}" yann@2034: done yann@2547: do_trace "Done adding basic lib dirs\n" yann@2190: do_trace "Scanning '/etc/ld.so.conf'\n" yann@2189: do_scan_etc_ldsoconf "${root}/etc/ld.so.conf" yann@2547: do_trace "Done scanning '/etc/ld.so.conf'\n" yann@2190: do_trace "Search path:\n" yann@2190: for p in "${needed_search_path[@]}"; do yann@2548: do_trace "-> '%s'\n" "${p}" yann@2190: done yann@2034: yann@2191: declare -a needed_list yann@2193: declare -a search_rpath yann@2548: do_trace "Scanning file '%s'\n" "${1}" yann@2034: do_process_file "${1}" yann@2548: do_trace "Done scanning file '%s'\n" "${1}"