Last active
August 29, 2015 14:21
-
-
Save astarasikov/355bf825f130fe4b5633 to your computer and use it in GitHub Desktop.
Visualize ELF dependency graph in Graphviz dot
This file contains hidden or 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
#!/usr/bin/env ruby2.1 | |
#@bin_dir="/home/me/bin/foo" | |
@bin_dir = "" | |
# context = { | |
# :exports => { | |
# "foo" => [ | |
# "file2.ois", | |
# "file3.ois", | |
# ] | |
# } | |
# :imports => { | |
# "file1.ois" => [ | |
# "foo", | |
# "bar" | |
# ] | |
# } | |
# } | |
# | |
def is_ignored_symbol(symname) | |
return true if symname.start_with? '.' | |
false | |
end | |
def store_export(ctx, filename, symname) | |
## strip local prefix | |
filename = filename.sub(@bin_dir, "") | |
ctx[:exports] ||= {} | |
ctx[:exports][symname] ||= [] | |
ctx[:exports][symname].push(filename) | |
end | |
def store_import(ctx, filename, symname) | |
## strip local prefix | |
filename = filename.sub(@bin_dir, "") | |
ctx[:imports] ||= {} | |
ctx[:imports][filename] ||= [] | |
ctx[:imports][filename].push(symname) | |
end | |
def parse_objdump_output(ctx, filename, objdump_output) | |
found_symtab = false | |
objdump_output.split("\n").each do |line| | |
if not found_symtab then | |
if line.start_with? "SYMBOL TABLE:" then | |
found_symtab = true | |
end | |
next | |
end | |
## empty line means we've reached the end of the table | |
break if line == "" | |
break if line == "no symbols" | |
columns = line.split(" ") | |
next if columns.length < 2 | |
addr = columns[columns.length - 2] | |
symname = columns[columns.length - 1] | |
next if is_ignored_symbol(symname) | |
if (addr.to_i(16) == 0) then | |
# zero address is import | |
store_import(ctx, filename, symname) | |
else | |
# non-zero address is export | |
store_export(ctx, filename, symname) | |
end | |
#print "#{addr} => #{symname}\n" | |
end | |
end | |
def add_file(ctx, file) | |
begin | |
objdump_output = `objdump -t #{file}` | |
parse_objdump_output(ctx, file, objdump_output) | |
rescue Exception => err | |
puts "add_file: #{err}" | |
end | |
end | |
def scan_dir(ctx, path) | |
Dir.foreach(path) do |item| | |
begin | |
next if item == '.' or item == '..' | |
file_path = path + '/' + item | |
add_file(ctx, file_path) if File.file? file_path | |
scan_dir(ctx, file_path) if File.directory? file_path | |
rescue Exception => err | |
puts "scan_dir: #{err}" | |
end | |
end | |
end | |
def print_dependency_link(from, to, by_symname) | |
print "\"#{from}\" -> \"#{to}\" [label=\"#{by_symname}\"]\n" | |
end | |
def print_dependency_grap(ctx) | |
imports = ctx[:imports] | |
return if nil == imports | |
print "digraph G {\n" | |
imports.each_key do |objname| | |
obj_data = imports[objname] | |
next if nil == obj_data | |
obj_data.each do |symname| | |
candidate_exports = ctx[:exports][symname] | |
if nil == candidate_exports then | |
print_dependency_link(objname, "UNDEFINED", symname) | |
next | |
end | |
candidate_exports.each do |tgt_objname| | |
print_dependency_link(objname, tgt_objname, symname) | |
end | |
end | |
end | |
print "}\n" | |
end | |
@bin_dir = ARGV[0] | |
@ctx = {} | |
scan_dir(@ctx, @bin_dir) | |
print_dependency_grap(@ctx) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment