summaryrefslogtreecommitdiff
path: root/scripts/xldd.in
diff options
context:
space:
mode:
Diffstat (limited to 'scripts/xldd.in')
-rw-r--r--scripts/xldd.in126
1 files changed, 123 insertions, 3 deletions
diff --git a/scripts/xldd.in b/scripts/xldd.in
index 0e8c0d0..8089426 100644
--- a/scripts/xldd.in
+++ b/scripts/xldd.in
@@ -16,6 +16,10 @@ fake_load_addr_rpath="$((0xdeadc0de))"
fake_load_addr_sysroot="$((0x8badf00d))"
ld_library_path="/lib:/usr/lib"
+need_e_class=
+need_e_flags=
+need_e_mach=
+
do_error() {
printf "%s: %s\n" "${my_name}" "$*" >&2
}
@@ -42,7 +46,7 @@ ldd (crosstool-NG) ${version}
Copyright (C) 2010 "Yann E. MORIN" <yann.morin.1998@free.fr>
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
+Licensed under the GPLv2, see the file LICENSE in the top-directory of the
sources for this package.
_EOF_
}
@@ -200,6 +204,103 @@ do_report_needed_found() {
"${sys}"
}
+# Helper: passed flags and needed flags must either both have
+# a given flag set, or neither must have it.
+flag_match() {
+ local flags="${1}"
+ local e_flags="${2}"
+ local dso="${3}"
+ local f
+
+ for f in ${flags}; do
+ case "${e_flags}:${need_e_flags}" in
+ *,${f},*:*,${f},*)
+ # Both have it, continue
+ ;;
+ *,${f},*)
+ # Only one has it
+ do_trace "-> skip incompatible '%s' (flags '%s', need '%s')" \
+ "${dso}" "${e_flags}" "${need_e_flags}"
+ return 1;;
+ *) ;; # Neither one has it, continue
+ esac
+ done
+ return 0
+}
+
+__warned_unknown_machine=no
+do_check_compat() {
+ local file="${1}"
+ local info e_mach e_class e_flags
+ local dso
+ local -a flags
+
+ if [ ! -r "${file}" ]; then
+ return 1
+ fi
+ info=`${readelf} -Wh "${file}"`
+ e_class=`echo "${info}" | "${sed}" -nr 's/.*Class:[[:space:]]*//p'`
+ e_mach=`echo "${info}" | "${sed}" -nr 's/.*Machine:[[:space:]]*//p'`
+ e_flags=`echo "${info}" | "${sed}" -nr -e 's/$/,/' -e 's/, /,/g' -e 's/.*Flags:[[:space:]]*0x[0-9a-f]{1,}//p'`
+ dso="${base}${d}/${needed}"
+ if [ "${e_class}" != "${need_e_class}" ]; then
+ do_trace "-> skip incompatible '%s' (class '%s', need '%s')\n" \
+ "${dso}" "${e_class}" "${need_e_class}"
+ return 1
+ fi
+ if [ "${e_mach}" != "${need_e_mach}" ]; then
+ do_trace "-> skip incompatible '%s' (machine '%s', need '%s')\n" \
+ "${dso}" "${e_mach}" "${need_e_mach}"
+ return 1
+ fi
+
+ # ElfXX_Ehdr.e_flags is trickier, the rules for compatibility are
+ # different for different architectures (machines). The logic is
+ # the same as in elf_machine_matches_host() in GNU libc.
+ case "${e_mach}" in
+ AArch64|"Advanced Micro Devices X86-64"|Alpha|ARM|"Intel 80386"|\
+ MC68000|"Xilinx MicroBlaze"|"Altera Nios II"|"Renesas / SuperH SH")
+ # Simple cases: flags are not taken into the account
+ ;;
+ "MIPS R3000")
+ # glibc just rejects "fp64", says it is unsupported on linux.
+ # but who knows, maybe one day ct-ng will be targeting non-linux too.
+ if ! flag_match "abi2 nan2008 fp64" "${e_flags}" "${dso}"; then
+ return 1
+ fi
+ ;;
+ PowerPC)
+ # For 32-bit PowerPC, flags not checked. For 64-bit, check ABI version:
+ # 0 means "not using any features affected by the differences"; v1 and v2
+ # are the defined ABI versions. Here we have a different check than glibc:
+ # in glibc, the dynamic linker checks the ABI compatibility against its own
+ # ABI while we here check against the binary ABI. Doing it properly requires
+ # a rework of this script, and the current check seems to work even with a
+ # trivial "hello world" (it is reported as "abiv1", so apparently no
+ # applications report 0 as ABI version).
+ if [ "${e_class}" = "ELF64" ] && ! flag_match "abiv1 abiv2" "${e_flags}" "${dso}"; then
+ return 1
+ fi
+ ;;
+ "IBM S/390")
+ # Dynamic linker checks against the kernel capability for "highgprs" flag
+ # at runtime, but we cannot do that. Report success.
+ ;;
+ Sparc|"Sparc v8+"|"Sparc v9")
+ # According to glibc sources, even DSOs reporting these different e_machine
+ # values may be compatible. We currently don't handle that, but might need to.
+ ;;
+ *)
+ # Warn once on an unhandled architecture and assume flags are compatible
+ if [ "${__warned_unknown_machine}" = "no" ]; then
+ __warned_unknown_machine=yes
+ do_error "Architecture ${e_mach} not handled by cross-ldd script; assuming match"
+ fi
+ ;;
+ esac
+ return 0
+}
+
# Search a needed file, scanning ${lib_dir} in the root directory
do_find_needed() {
local needed="${1}"
@@ -229,7 +330,7 @@ do_find_needed() {
fi
for d in "${dirs[@]}"; do
do_trace "-> looking in '%s' (%s)\n" "${d}" "${where}"
- if [ -f "${base}${d}/${needed}" ]; then
+ if do_check_compat "${base}${d}/${needed}"; then
found="${d}/${needed}"
do_trace "---> found\n"
break 2
@@ -250,9 +351,11 @@ do_find_needed() {
# Scan a file for all NEEDED tags
do_process_file() {
local file="${1}"
+ local initial="${2}"
local -a save_search_rpath
local n m
local found
+ local info
do_trace "Parsing file '%s'\n" "${file}"
@@ -270,6 +373,17 @@ do_process_file() {
done
do_trace ": end search path\n"
+ if [ -n "${initial}" ]; then
+ if ! "${readelf}" -Wl "${file}" |
+ "${grep}" 'Requesting program interpreter: ' >/dev/null; then
+ printf " not a dynamic executable\n"
+ exit 1
+ fi
+ info=`${readelf} -Wh "${file}"`
+ need_e_class=`echo "${info}" | "${sed}" -nr 's/.*Class:[[:space:]]*//p'`
+ need_e_mach=`echo "${info}" | "${sed}" -nr 's/.*Machine:[[:space:]]*//p'`
+ need_e_flags=`echo "${info}" | "${sed}" -nr -e 's/$/,/' -e 's/, /,/g' -e 's/.*Flags:[[:space:]]*0x[0-9a-f]{1,}//p'`
+ fi
for n in $( "${readelf}" -d "${file}" \
|"${grep}" -E '\(NEEDED\)' \
|"${sed}" -r -e 's/^.*Shared library:[[:space:]]+\[([^]]+)\].*/\1/;' \
@@ -324,6 +438,12 @@ do_scan_etc_ldsoconf() {
do_trace "Finished parsing ld.so.conf: '%s'\n" "${ldsoconf}"
}
+if [ -z "${1}" ]; then
+ show_help
+elif [ ! -r "${1}" ]; then
+ do_error "${1}: No such file or directory"
+fi
+
# Build up the full list of search directories
declare -a needed_search_path
do_trace "Adding basic lib dirs\n"
@@ -348,5 +468,5 @@ done
declare -a needed_list
declare -a search_rpath
do_trace "Scanning file '%s'\n" "${1}"
-do_process_file "${1}"
+do_process_file "${1}" initial
do_trace "Done scanning file '%s'\n" "${1}"