-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
5 changed files
with
346 additions
and
0 deletions.
There are no files selected for viewing
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
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
TGT=`which glxgears` | ||
|
||
all: | ||
./sotrace.py $(TGT) out.dot | ||
dot -Tsvg -o out.svg out.dot | ||
-display out.svg | ||
|
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,34 @@ | ||
# sotrace | ||
Traces the shared-object dependencies of a binary, and graphs them. | ||
|
||
## Usage | ||
|
||
``` | ||
./sotrace.py /path/to/foo out.dot | ||
dot -Tout.svg out.dot | ||
eog out.svg | ||
``` | ||
|
||
 | ||
|
||
## Rationale | ||
|
||
Dynamically linked binaries pull in a large amount of dependencies. | ||
It is often hard to get a good idea of what gets pulled in, recursively. | ||
|
||
This tool will create a graphic that clearly shows the dependencies. | ||
|
||
It is also a useful tool to identify software bloat. | ||
|
||
## Limitations | ||
|
||
Currently, it will only find dependencies that are dynamically linked. | ||
|
||
This means it will miss: | ||
* statically linked dependencies. | ||
* plugins (.so files that are loaded at run-time, not load-time.) | ||
|
||
## Author | ||
|
||
Bram Stolk [email protected] | ||
|
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,81 @@ | ||
#!/bin/env python3 | ||
# | ||
# sotrace.py | ||
# | ||
# shared object trace | ||
# | ||
# Usage: | ||
# sotrace.py /path/to/binary out.dot | ||
# dot -Tout.svg out.dot | ||
# | ||
# (c)2024 Bram Stolk | ||
|
||
|
||
import os # For popen | ||
import sys # For argv | ||
|
||
|
||
# Given a target library name, see what this library directly depends on. | ||
def dep_list(tgt) : | ||
cmd = "readelf -d " + tgt + " | grep NEEDED" | ||
f = os.popen(cmd, 'r') | ||
lines = f.readlines() | ||
f.close() | ||
vals = [ x.split(": ")[1].strip() for x in lines ] | ||
deps = [ x[1:-1] for x in vals ] | ||
return deps | ||
|
||
|
||
# Given a set of dependency names, check to what path the are resolved using ldd | ||
def dep_to_lib(tgt, deps) : | ||
cmd = "ldd " + tgt | ||
f = os.popen(cmd, 'r') | ||
lines = f.readlines() | ||
f.close() | ||
mapping = {} | ||
for line in lines: | ||
if "=>" in line: | ||
parts = line.strip().split(" => ") | ||
nam = parts[0].strip() | ||
if nam in deps : | ||
mapping[nam] = parts[1].split(" (")[0] | ||
return mapping | ||
|
||
|
||
# Walk the dependencies of the target, and write the graph to file. | ||
def traverse_so(tgt, nam, f, depth, visited, linked) : | ||
deps = dep_list(tgt) | ||
lib_map = dep_to_lib(tgt, deps) | ||
|
||
for val in lib_map.keys() : | ||
if nam != val and (nam,val) not in linked: | ||
linked.append( (nam,val) ) | ||
f.write('"' + nam + '" -> "' + val + '"\n') | ||
|
||
for dep in deps: | ||
if dep in lib_map: | ||
m = lib_map[dep] | ||
if not m in visited: | ||
visited.append(m) | ||
dnam = os.path.basename(dep) | ||
traverse_so(m, dnam, f, depth+1, visited, linked) | ||
|
||
# Main entry point | ||
if __name__ == '__main__' : | ||
|
||
if len(sys.argv) != 3 : | ||
print("Usage:", sys.argv[0], "libfoo.so graph.dot") | ||
sys.exit(1) | ||
|
||
tgt = sys.argv[1] | ||
out = sys.argv[2] | ||
nam = os.path.basename(tgt) | ||
|
||
f = open(out, "w") | ||
f.write("digraph G {\n") | ||
f.write(" rankdir = LR;\n") | ||
traverse_so(tgt, nam, f, 0, [], []) | ||
f.write("}\n"); | ||
f.close() | ||
|
||
|