Last active
September 4, 2022 17:39
-
-
Save baryluk/ddce950c825eb0966aec96ceaf58319b to your computer and use it in GitHub Desktop.
Graph binary / library / dynamic library / shared object / so / ELF files, dependencies as a graph
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/bin/bash | |
# NOTE: Deprecated. Use https://gist.github.com/baryluk/09cbabb215351117b32aee994e5619a0 instead | |
# This small program takes one parameter, a binary (or library), and outputs | |
# a dependency graph. This is done recursively for all subdependencies. | |
# Some common dependencies are ignored like this ones to glibc basic libraries. | |
# The ones related to stdc++ / gcc are not ignored. | |
# After script is done execute this command to generate dependency graph: | |
# | |
# (echo "digraph {"; ./analyzer_binary.sh ${BINARY} | sort | uniq; echo "}") | dot -Grankdir=LR -Nshape=box -Tpng -o dependencies.png /dev/fd/0 | |
# | |
# Copyright: Witold Baryluk, 2019. MIT license | |
# TODO(baryluk): Automatically use proper architectures. | |
# TODO(baryluk): Rewrite in Python3 / Go / D. | |
# TODO(baryluk): Make it more parallel. | |
# TODO(baryluk): Don't call recursively, but process things iteratively using | |
# a queue and arrays. Don't process dependencies we already processed before | |
# (or are processing in parallel). | |
# TODO(baryluk): Use `/lib/ld-linux.so.2 --list`, /lib64/ld-linux-x86-64.so.2 --list or ldd directly somehow? | |
# TODO(baryluk): Name it something short. ldx? ldv? lld? | |
# TODO(baryluk): Also traverse LD_LIBRARY_PATH | |
# TODO(baryluk): Support deprecated DT_RPATH just in case. | |
# TODO(baryluk): Support dependencies with slashes, which indicate to not search | |
# for them in RPATH, RUNPATH or system default search paths, but instead to use | |
# the path directly (it can be relative or absolute. When relative is the path | |
# relative to the main executable path, or to the library that is using it?) | |
# | |
# LD_DEBUG=libs,files /bin/ls 2>&1 | |
# LD_DEBUG=libs,files LD_DEBUG_OUTPUT=output_log.txt /bin/ls | |
# | |
# Can be used to capture so info, but it is unsecure. | |
# | |
# Similarly `ld-linux.so.2 --list` and `ldd` are not secure. | |
# ldd /bin/ls is basically equivalent do running LD_TRACE_LOADED_OBJECTS=1 ld-linux.so.2 /bin/ls | |
# In fact ldd is just a bash script that detects elf object architecture and invokes | |
# proper dynamic linker with proper flags and environment variables. | |
# but dynamic linker will most likely still invoke init/fini sections of elf objects! | |
BINARY=$1 | |
NAME=${2:-$BINARY} | |
INDENT="" | |
LEVEL=${3:-0} | |
ORGBINARY="${BINARY}" | |
for i in $(seq ${LEVEL}); do | |
INDENT="${INDENT} " | |
done | |
if ! [ -f "${BINARY}" ]; then | |
while read path; do | |
if [ -f "${path}/${BINARY}" ]; then | |
BINARY="${path}/${BINARY}" | |
break | |
fi | |
done <<<$(grep -E -v '^#' /etc/ld.so.conf.d/i386-linux-gnu.conf) | |
fi | |
DEPENDENCIES=$(objdump -p "${BINARY}" | grep -E '\bNEEDED\b' | awk '{print $2;}') | |
# Also known as DT_RUNPATH | |
RUNPATH=$(objdump -p "${BINARY}" | grep -E '\bRUNPATH\b' | awk '{print $2;}') | |
if [ "x${RUNPATH}" != "x" ]; then | |
RUNPATH="${RUNPATH}/" | |
fi | |
IGNORED="ld-linux.so.2 libc.so.6 libm.so.6 libdl.so.2 libpthread.so.0 librt.so.1" | |
for d in ${DEPENDENCIES} | |
do | |
if echo "${IGNORED}" | grep -E "\b${d}\b" >/dev/null; then | |
continue | |
fi | |
# echo "${INDENT}" $d | |
echo "\"${NAME}\" -> \"${d}\";" | |
if [ -f "${RUNPATH}${d}" ]; then | |
$0 "${RUNPATH}${d}" "${d}" $((${LEVEL}+1)) | |
else | |
$0 "${d}" "${d}" $((${LEVEL}+1)) | |
fi | |
done | sort | uniq # This is pretty inefficient (sorting and sorting over and over again), but does work and isn't too slow. |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment