xref: /freebsd/tools/build/check-links.sh (revision 29fc4075e69fd27de0cded313ac6000165d99f8b)
1#!/bin/sh
2# $FreeBSD$
3
4libkey() {
5	libkey="lib_symbols_$1"
6	patterns=[.+,/-]
7	replacement=_
8	while :; do
9		case " ${libkey} " in
10			*${patterns}*)
11				libkey="${libkey%%${patterns}*}${replacement}${libkey#*${patterns}}"
12				;;
13			*)
14				break
15				;;
16		esac
17	done
18	return 0
19}
20
21usage() {
22	cat <<-EOF
23	usage: $0 [-Uv] [-L LD_LIBRARY_PATH] file
24	       -L:       Specify an alternative LD_LIBRARY_PATH for the library resolution.
25	       -U:       Skip looking for unresolved symbols.
26	       -v:       Show which library each symbol is resolved to.
27	EOF
28	exit 0
29}
30
31ret=0
32CHECK_UNRESOLVED=1
33VERBOSE_RESOLVED=0
34while getopts "L:Uv" flag; do
35	case "${flag}" in
36		L) LIB_PATH="${OPTARG}" ;;
37		U) CHECK_UNRESOLVED=0 ;;
38		v) VERBOSE_RESOLVED=1 ;;
39		*) usage ;;
40	esac
41done
42shift $((OPTIND-1))
43
44if ! [ -f "$1" ]; then
45	echo "No such file or directory: $1" >&2
46	exit 1
47fi
48
49mime=$(file -L --mime-type $1)
50isbin=0
51case $mime in
52*application/x-executable|*application/x-pie-executable) isbin=1 ;;
53*application/x-sharedlib);;
54*) echo "Not an elf file" >&2 ; exit 1;;
55esac
56
57# Gather all symbols from the target
58unresolved_symbols=$(nm -u -D --format=posix "$1" | awk '$2 == "U" {print $1}' | tr '\n' ' ')
59[ ${isbin} -eq 1 ] && bss_symbols=$(nm -D --format=posix "$1" | awk '$2 == "B" && $4 != "" {print $1}' | tr '\n' ' ')
60if [ -n "${LIB_PATH}" ]; then
61	for libc in /lib/libc.so.*; do
62		LDD_ENV="LD_PRELOAD=${libc}"
63	done
64	LDD_ENV="${LDD_ENV} LD_LIBRARY_PATH=${LIB_PATH}"
65fi
66
67ldd_libs=$(env ${LDD_ENV} ldd $(realpath $1) | awk '{print $1 ":" $3}')
68
69# Check for useful libs
70list_libs=
71resolved_symbols=
72for lib in $(readelf -d $1 | awk '$2 ~ /\(?NEEDED\)?/ { sub(/\[/,"",$NF); sub(/\]/,"",$NF); print $NF }'); do
73	echo -n "checking if $lib is needed: "
74	if [ -n "${lib##/*}" ]; then
75		for libpair in ${ldd_libs}; do
76			case "${libpair}" in
77				${lib}:*) libpath="${libpair#*:}" && break ;;
78			esac
79		done
80	else
81		libpath="${lib}"
82	fi
83	list_libs="$list_libs $lib"
84	foundone=
85	lib_symbols="$(nm -D --defined-only --format=posix "${libpath}" | awk '$2 ~ /C|R|D|T|W|B|V/ {print $1}' | tr '\n' ' ')"
86	if [ ${CHECK_UNRESOLVED} -eq 1 ]; then
87		# Save the global symbols for this lib
88		libkey "${lib}"
89		setvar "${libkey}" "${lib_symbols}"
90	fi
91	for fct in ${lib_symbols}; do
92		case " ${unresolved_symbols} ${bss_symbols} " in
93			*\ ${fct}\ *) foundone="${fct}" && break ;;
94		esac
95	done
96	if [ -n "${foundone}" ]; then
97		echo "yes... ${foundone}"
98	else
99		echo "no"
100		ret=1
101	fi
102done
103
104if [ ${CHECK_UNRESOLVED} -eq 1 ]; then
105	# Add in crt1 symbols
106	list_libs="${list_libs} crt1.o"
107	lib_symbols="$(nm --defined-only --format=posix "/usr/lib/crt1.o" | awk '$2 ~ /C|R|D|T|W|B|V/ {print $1}' | tr '\n' ' ')"
108	# Save the global symbols for this lib
109	libkey "crt1.o"
110	setvar "${libkey}" "${lib_symbols}"
111
112	# Now search libs for all symbols and report missing ones.
113	for sym in ${unresolved_symbols}; do
114		found=0
115		for lib in ${list_libs}; do
116			libkey "${lib}"
117			eval "lib_symbols=\"\${${libkey}}\""
118			# lib_symbols now contains symbols for the lib.
119			case " ${lib_symbols} " in
120				*\ ${sym}\ *)
121					[ ${VERBOSE_RESOLVED} -eq 1 ] &&
122					    echo "Resolved symbol ${sym} from ${lib}"
123					found=1
124					break
125					;;
126			esac
127		done
128		if [ $found -eq 0 ]; then
129			echo "Unresolved symbol $sym"
130			ret=1
131		fi
132	done
133fi
134
135exit ${ret}
136