scripts/xldd.in
author Daniel Price <daniel.price@gmail.com>
Tue Nov 20 16:59:17 2012 -0800 (2012-11-20)
changeset 3126 333d3e40cbd1
parent 3025 d25fb5bf6615
child 3134 863723936e24
permissions -rwxr-xr-x
scripts: refine static linking check to better guide the user

The current mechanism to check if static linking is possible, and the mesage
displayed on failure, can be puzzling to the unsuspecting user.

Also, the current implementation is not using the existing infrastructure,
and is thus difficult to enhance with new tests.

So, switch to using the standard CT_DoExecLog infra, and use four tests to
check for the host compiler:
- check we can run it
- check it can build a trivial program
- check it can statically link that program
- check if it statically link with libstdc++

That should cover most of the problems. Hopefully.

(At the same time, fix a typo in a comment)

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