scripts/xldd.in
author "Yann E. MORIN" <yann.morin.1998@anciens.enib.fr>
Wed Nov 24 21:01:46 2010 +0100 (2010-11-24)
changeset 2191 226d9684bf3c
parent 2190 9b9a0bb51cfb
child 2192 cbd4539a86ca
permissions -rwxr-xr-x
scripts/xldd: avoid reporting duplicates

Once a NEEDED dependency has been solved, do not report it
if other dependencies depend on it.

Signed-off-by: "Yann E. MORIN" <yann.morin.1998@anciens.enib.fr>
     1 #!@@CT_bash@@
     2 
     3 # NON-CONFIGURABLE STUFF!
     4 export LC_ALL=C
     5 version="@@CT_VERSION@@"
     6 bits="@@CT_BITS@@"
     7 sed="@@CT_sed@@"
     8 grep="@@CT_grep@@"
     9 
    10 my_name="$( basename "${0}" )"
    11 prefix="${0%-ldd}"
    12 gcc="${prefix}-gcc"
    13 readelf="${prefix}-readelf"
    14 fake_load_addr="$((0xdeadbeef))"
    15 fake_load_addr_sys="$((0x8badf00d))"
    16 ld_library_path="/lib:/usr/lib"
    17 
    18 do_error() {
    19     printf "%s: %s\n" "${my_name}" "$*" >&2
    20 }
    21 
    22 do_opt_error() {
    23     do_error "$@"
    24     printf "Try \`%s --help' for more information\n" >&2
    25 }
    26 
    27 do_trace() {
    28     local depth=0
    29 
    30     [ -z "${CT_XLDD_VERBOSE}" ] && return 0
    31 
    32     for((depth=0; "${#FUNCNAME[$((depth+1))]}" != 0; depth++)); do :; done
    33     printf "%*s" $((4*(depth-1))) "" >&2
    34     printf -- "$@" >&2
    35 }
    36 
    37 show_version() {
    38     # Fake a real ldd, just in case some dumb script would check
    39     cat <<_EOF_
    40 ldd (crosstool-NG) ${version}
    41 Copyright (C) 2010 "Yann E. MORIN" <yann.morin.1998@anciens.enib.fr>
    42 This is free software; see the source for copying conditions.  There is NO
    43 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
    44 Licensed under the GPLv2, see the file LICENSES in the top-directory of the
    45 sources for this package.
    46 _EOF_
    47 }
    48 
    49 show_help() {
    50     cat <<_EOF_
    51 Usage: ${my_name} [OPTION]... --root DIR FILE...
    52       --help              print this help and exit
    53       --version           print version information and exit
    54       --root dir          treat dir as being the root of the target
    55   -s, --show-system       mark libs from the sysroot with a trailing '[*]'
    56 
    57 _EOF_
    58     cat <<_EOF_ |fmt
    59 ${my_name} tries to mimick the behavior of a real native ldd, but can be
    60 used in a cross-development environment. Here is how it differs from a
    61 real native ldd:
    62 
    63 If the CT_XLDD_DEBUG variable is set and non-empty, then ${myname} will
    64 print a lot of debug messages, explaining how it builds the library
    65 search path, and how each library was found and why.
    66 
    67 The LD_LIBRARY_PATH variable is not used, as it can not reliably be
    68 guessed except at runtime, and we can't run.
    69 
    70 ${my_name} does not scan /etc/ld.so.cache, but instead uses /etc/ld.so.conf
    71 (it understands the include directives therein for libces that have that).
    72 
    73 ${my_name} will search the directory specified with --root for libraries
    74 to resolve the NEEDED tags. If --root is not set, then ${my_name} will
    75 use the value in the environment variable \${CT_XLDD_ROOT}. If neither
    76 is set, then this is an error.
    77 
    78 If NEEDED libraries can't be found in the specified root directory, then
    79 ${my_name} will also look in the sysroot of the toolchain to see if it
    80 can find them.
    81 
    82 For NEEDED libraries that were found, the output will look like:
    83         libneeded.so => /path/to/libneeded.so (0xloadaddr)
    84 
    85 and for those that were not found, the output will look like:
    86         libneeded.so not found
    87 
    88 The paths are relative to the specified root directory, or to the sysroot
    89 (eg. /lib/libneeded.so, /usr/lib/libneeded.so, and so on...).
    90 
    91 The expected load address 'loadaddr' is a faked address to match the output
    92 of the real ldd, but has no actual meaning (set to some constants for now,
    93 0x8badf00d for libraries from the sysroot, 0xdeadbeef for others).
    94 _EOF_
    95 
    96 # Unimplemeted yet:
    97 #  -d, --data-relocs       process data relocations
    98 #  -r, --function-relocs   process data and function relocations
    99 #  -u, --unused            print unused direct dependencies
   100 #  -v, --verbose           print all information
   101 
   102 # See also this thread:
   103 #  http://sourceware.org/ml/crossgcc/2008-09/msg00057.html
   104 }
   105 
   106 # Parse command line options
   107 root="${CT_XLDD_ROOT}"
   108 show_system=
   109 while true; do
   110     case "${1}" in
   111         --help)
   112             show_help
   113             exit 0
   114             ;;
   115         --version)
   116             show_version
   117             exit 0
   118             ;;
   119         --root)
   120             root="$2"
   121             shift
   122             ;;
   123         --root=*)
   124             root="${1#--root=}"
   125             ;;
   126         --show-system|-s)
   127             show_system=1
   128             ;;
   129         -*)
   130             do_opt_error "unrecognized option \`${1}'"
   131             exit 1
   132             ;;
   133         *)
   134             break
   135             ;;
   136     esac
   137     shift
   138 done
   139 
   140 # Sanity checks
   141 if [ -z "${root}" ]; then
   142     do_opt_error "no root given"
   143     exit 1
   144 fi
   145 if [ ! -d "${root}" ]; then
   146     do_error "\`${root}': no such file or directory"
   147     exit 1
   148 fi
   149 
   150 sysroot="$( "${gcc}" -print-sysroot 2>/dev/null )"
   151 if [ -z "${sysroot}" ]; then
   152     sysroot="$( "${gcc}" -print-file-name=libc.so 2>/dev/null   \
   153                 |sed -r -e 's:/usr/lib/libc.so$::;'             \
   154               )"
   155 fi
   156 if [ -z "${sysroot}" ]; then
   157     do_error "unable to find sysroot for \`${gcc}'"
   158 fi
   159 
   160 do_report_needed_found() {
   161     local needed="${1}"
   162     local path="${2}"
   163     local system="${3}"
   164     local loadaddr
   165     local sys
   166 
   167     if [ -z "${system}" ]; then
   168         loadaddr="${fake_load_addr}"
   169     else
   170         loadaddr="${fake_load_addr_sys}"
   171         if [ -n "${show_system}" ]; then
   172             sys=" [*]"
   173         fi
   174     fi
   175 
   176     printf "%8s%s => %s (0x%0*x)%s\n"   \
   177            ""                           \
   178            "${needed}"                  \
   179            "${path}"                    \
   180            "$((bits/4))"                \
   181            "${loadaddr}"                \
   182            "${sys}"
   183 }
   184 
   185 # Search a needed file, scanning ${lib_dir} in the root directory
   186 do_find_needed() {
   187     local needed="${1}"
   188     local found
   189     local found_sysroot
   190     local d
   191 
   192     do_trace "Searching for '${needed}'\n"
   193 
   194     for d in "${needed_search_path[@]}"; do
   195         do_trace "-> looking in '${d}'\n"
   196         if [ -f "${root}${d}/${needed}" ]; then
   197             found="${d}/${needed}"
   198             do_trace "---> found\n"
   199             break
   200         fi
   201     done
   202     if [ -z "${found}" ]; then
   203     for d in "${needed_search_path[@]}"; do
   204         do_trace "-> looking in '${d}' (sysroot)\n"
   205         if [ -f "${sysroot}${d}/${needed}" ]; then
   206             found_sysroot="${d}/${needed}"
   207             do_trace "---> found\n"
   208             break
   209         fi
   210     done
   211     fi
   212 
   213     if [ -n "${found}" ]; then
   214         do_report_needed_found "${needed}" "${found}"
   215         do_process_file "${root}${found}"
   216     elif [ -n "${found_sysroot}" ]; then
   217         do_report_needed_found "${needed}" "${found_sysroot}" "sys"
   218         do_process_file "${sysroot}${found_sysroot}"
   219     else
   220         printf "%8c%s not found\n" "" "${needed}"
   221     fi
   222 }
   223 
   224 # Scan a file for all NEEDED tags
   225 do_process_file() {
   226     local file="${1}"
   227     local n m
   228     local found
   229 
   230     do_trace "Parsing file '${file}'\n"
   231 
   232     for n in $( "${readelf}" -d "${file}"                                           \
   233                 |"${grep}" -E '\(NEEDED\)'                                          \
   234                 |"${sed}" -r -e 's/^.*Shared library:[[:space:]]+\[(.*)\]$/\1/;'    \
   235               ); do
   236         found=0
   237         for m in "${needed_list[@]}"; do
   238             [ "${n}" = "${m}" ] && found=1 && break
   239         done
   240         if [ ${found} -ne 0 ]; then
   241             do_trace "-> skipping already known dependency '${n}'\n"
   242             continue
   243         fi
   244         do_trace "-> handling new dependency '${n}'\n"
   245         needed_list+=( "${n}" )
   246         do_find_needed "${n}"
   247      done
   248 }
   249 
   250 # Recursively scan a /etc/ld.so.conf file
   251 do_scan_etc_ldsoconf() {
   252     local ldsoconf="${1}"
   253     local g
   254     local f
   255 
   256     [ -f "${ldsoconf}" ] || return 0
   257     do_trace "Parsing ld.so.conf: '${ldsoconf}'\n"
   258 
   259     while read line; do
   260         case "${line}" in
   261             include\ *)
   262                 g="${root}${line#include }"
   263                 do_trace "-> handling include directive '${g}'\n"
   264                 for f in ${g}; do
   265                     do_scan_etc_ldsoconf "${f}"
   266                 done
   267                 ;;
   268             \#*|"")
   269                 ;;
   270             *)
   271                 do_trace "-> adding search dir '${line}'\n"
   272                 needed_search_path+=( "${line}" )
   273                 ;;
   274         esac
   275     done <"${ldsoconf}"
   276 }
   277 
   278 # Build up the full list of search directories
   279 declare -a needed_search_path
   280 do_trace "Adding basic lib dirs\n"
   281 ld_library_path="${ld_library_path}:"
   282 while [ -n "${ld_library_path}" ]; do
   283     d="${ld_library_path%%:*}"
   284     if [ -n "${d}" ]; then
   285         do_trace "-> adding search dir '${d}'\n"
   286         needed_search_path+=( "${d}" )
   287     fi
   288     ld_library_path="${ld_library_path#*:}"
   289 done
   290 do_trace "Scanning '/etc/ld.so.conf'\n"
   291 do_scan_etc_ldsoconf "${root}/etc/ld.so.conf"
   292 do_trace "Search path:\n"
   293 for p in "${needed_search_path[@]}"; do
   294     do_trace "-> '${p}'\n"
   295 done
   296 
   297 do_trace "Scanning file '${1}'\n"
   298 declare -a needed_list
   299 do_process_file "${1}"