scripts/xldd.in
author "Yann E. MORIN" <yann.morin.1998@anciens.enib.fr>
Sat Apr 30 23:13:12 2011 +0200 (2011-04-30)
changeset 2424 28092887b486
parent 2192 cbd4539a86ca
child 2546 ecd9d85a4f2e
permissions -rwxr-xr-x
scripts: fix installation

Changeset e013ddebc063 forgot to install the samples.mk file.

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