aboutsummaryrefslogtreecommitdiff
path: root/lddsafe
blob: a39c009d2610c826262e26ef102e9d05da7cfd0e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
#!/usr/bin/env bash
#
# lddsafe - Safely print shared library dependencies (similar to ldd).
# Author: Ricardo Garcia Gonzalez.
# Author: Ivan Mironov.
# License: Public domain code.
#

# Better for grep and expected output of other tools.
export LANG=C

# Expand globs to nothing when no match is found.
shopt -s nullglob

# Print error and exit.
die()
{
	echo "ERROR: $1" 1>&2
	exit 1
}

# Find required program.
findexec()
{
	which "$1" >/dev/null 2>/dev/null || die "$1 not found"
}

# Check file exists and is readable.
checkreadable()
{
	[ -r "$1" ] || die "$1 missing or not readable"
}

# Check for bash version.
[ -n "$BASH_VERSION" ] || die "Bash required"
[ ${BASH_VERSINFO[0]} -ge 4 ] || die "Bash version 4 or later required"

# Check needed programs.
findexec objdump
findexec readelf
findexec dirname
findexec sed

# Check needed files.
checkreadable /etc/ld.so.conf

# Check arguments.
[ "$1" == "-n" ] && recursive=0 || recursive=1
[ "$1" == "-n" ] && shift
if [ $# -eq 0 ]; then
	echo "Usage: $( basename $0 ) [-n] FILE..." 2>&1
	exit 1
fi
for arg in "$@"; do
	checkreadable "$1"
done

# Recursively print the list of files included from /etc/ld.so.conf.
ld_so_conf_deps()
{
	echo "$1"
	dirname="$( dirname "$1" )"
	patterns="$( sed -n 's/^include[\ \t]\+\(.\+\)$/\1/p' "$1" )"
	set -o noglob
	for pattern in $patterns; do
		set +o noglob
		case $pattern in
			/*)
				for file in $pattern; do
					ld_so_conf_deps "$file"
				done
				;;
			*)
				for file in $dirname/$pattern; do
					ld_so_conf_deps "$file"
				done
				;;
		esac
	done
	set +o noglob
}

# Additional library directories.
LD_LIBRARY_PATH_LIBS="${LD_LIBRARY_PATH//:/ }"
MORELIBDIRS="$( sed '/^include[\ \t]/d' $( ld_so_conf_deps /etc/ld.so.conf ) )"

# Search for a given library name.
searchlib()
{
	found=0
	for libdir in $LIBDIRS; do
		path="$libdir"/"$1"
		if [ -r "$path" ]; then
			found=1
			break
		fi
	done
	[ $found -eq 1 ] && echo "$path"
}

# Already visited libraries.
declare -A VISITEDLIBS

# Print dependency results, recursively.
recursivedeps()
{
	for lib in $( objdump -p "$1" | \
		      sed -n 's,^ *NEEDED \+\([^ ]\+\) *$,\1,p' ); do
		if [ ! "${VISITEDLIBS[$lib]}" ]; then
			VISITEDLIBS["$lib"]=1
			file=`searchlib "$lib"`
			if [ "$file" ]; then
				echo "	$lib => $file"
				[ $recursive -eq 1 ] && recursivedeps "$file"
			else
				echo "	$lib => not found"
			fi
		fi
	done
}

# Search symbol names in library directories.
for arg in "$@"; do
	# Print file name when more than one file given.
	[ $# -gt 1 ] && echo "${arg}:"

	# Set appropriate library search directories.
	class=$( readelf -h $arg 2>/dev/null | \
		 sed -n 's/^[ \t]*Class:[\ \t]\+\(.\+\)/\1/p' )
	if [ -z "$class" ]; then
		echo "$arg: not an ELF file" 2>&1
		continue
	fi
	if [ $class != ELF32 -a $class != ELF64 ]; then
		echo "$arg: unknown ELF format" 2>&1
		continue
	fi
	if [ $class == ELF64 ]; then
		if [ -d /lib64 ]; then
			stdlibs="/lib64 /usr/lib64"
		else
			stdlibs="/lib /usr/lib"
		fi
	else # $class == ELF32
		if [ -d /lib32 ]; then
			stdlibs="/lib32 /usr/lib32"
		else
			stdlibs="/lib /usr/lib"
		fi
	fi
	LIBDIRS="$LD_LIBRARY_PATH_LIBS $MORELIBDIRS $stdlibs"

	# Get a unique list of library dependencies for this argument.
	unset VISITEDLIBS
	declare -A VISITEDLIBS
	recursivedeps "$arg"
done
exit 0