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