#!/usr/bin/python # # Given a file, compute its list of unused ELF dependencies. Unlike # ldd -r -u, take the complete load stack into account. # # For example. evolution is linked against (among many other things) # libnspr4.so and libdl.so.2. ldd -r -u correctly notes that libdl is # an unused direct dependency, since evo does not call into libdl itself. # However, libnspr4.so _does_ call into libdl, so libdl isn't unused, # just not directly used. # # In implementation news: who needs data structures when you can memoize. import os import sys # ldd's output is transitively complete, which is pleasant. by this i mean, # it doesn't just print out the DT_NEEDED from the binary, but recurses down # those to find everything else downstream of it. _depstable = {} def deps(name): ret = [] if name in _depstable.keys(): return _depstable[name] file = os.popen("/usr/bin/ldd %s" % name) for line in file: line = line.partition("=>")[2].partition("(")[0].strip() if line: ret.append(line) _depstable[name] = ret return ret _uddtable = {} # ldd -r -u, however, is not recursive. def unused_direct_deps(name): ret = [] if name in _uddtable.keys(): return _uddtable[name] file = os.popen("/usr/bin/ldd -r -u %s 2>/dev/null" % name) for line in file: line = line.strip() if not line.startswith("/"): continue ret.append(line) _uddtable[name] = ret return ret def used_direct_deps(name): used = set(deps(name)).difference(set(unused_direct_deps(name))) return used _udtable = {} def used_deps(name): if name in _udtable.keys(): return _udtable[name] all = set(deps(name)) definite = used_direct_deps(name) for i in all: definite.update(used_deps(i)) _udtable[name] = definite return _udtable[name] def unused_deps(name): all = set(deps(name)) return all.difference(used_deps(name)) for i in sys.argv[1:]: if not os.access(i, os.F_OK): continue foo = unused_deps(i) if not len(foo): continue print "%s:" % i for j in foo: print " %s" % j print