#!/usr/bin/python2.4 import sys import os import re import popen2 import pkg_tools def safe_popen(cmd): proc = popen2.Popen3(cmd) output = proc.fromchild.read() ret = proc.wait() assert ret == 0 return output class Library: def __init__(self, soname, search): self.soname = soname self.search = search self.warning = False self._get_filename() self._get_symbols() def _search_all(self, soname): output = safe_popen(['dpkg', '-S', soname]) output = output.split('\n') output = [line for line in output if line] output = [i.split(': ', 1) for i in output] if len(output) == 0: raise RuntimeError("Error: Library not found (1): " + self.soname) elif len(output) == 1: return output[0][1] # more than 1 pkg found o_dict = pkg_tools.dictify(output) for pkg in self.search: if pkg in o_dict: res = o_dict[pkg] res = [i for i in res if os.path.basename(i) == self.soname] if len(res) > 1: raise RuntimeError( "Error: More than one library with same soname '%s' " % self.soname + "in package '%s'\n" % pkg + repr(res)) return o_dict[pkg][0] self.warning = True print >> sys.stderr, "Warning: Choosing arbitrary library out of multiple possible" print >> sys.stderr, " Lib: %s, Alternatives: %r" % (self.soname, output) return output[0][1] def _get_filename(self): filename = None for path in ['/usr/lib', '/lib']: lib = os.path.join(path, self.soname) if os.path.exists(lib): filename = lib break if filename: self.filename = filename return self.filename = self._search_all(self.soname) def _get_symbols(self): output = safe_popen(["nm", "-D", "-g", self.filename]) output = output.split('\n') output = [i.split(' ')[2] for i in output if i and not i.startswith(' ')] self.symbols = output def f(): while True: yield None self.symbols_dict = dict(zip(self.symbols, f())) class Binary: def __init__(self, filename, Packages): self.filename = filename self.Packages = Packages self.warning = False self.package = self._get_package() self.depends = self._get_depends() self.lib_search = [self.package] + self.depends self._get_needed() self._make_libs() self._get_reloc() self._ignore_syms = {'__gmon_start__': None} def _get_depends(self): return self.Packages[self.package]['*Depends'] def _parse_diversion(self, output): output = output.split('\n') count = 0 for line in output: if not line: continue div_from = re.search(r"^diversion by (\S*) from: (.*)$", line) div_to = re.search(r"^diversion by (\S*) to: (.*)$", line) if div_from: pkg_from, file_from = div_from.groups() continue if div_to: pkg_to, file_to = div_to.groups() continue count += 1 pre, pkg_file = line.split(': ', 1) packages = pre.split(', ') assert count < 2 assert pkg_from == pkg_to if count: assert pkg_file == file_from assert self.filename == file_from return pkg_to else: assert self.filename == file_to output = safe_popen(['dpkg', '-S', file_from]) output = output.split('\n') output = [i for i in output if i and not i.startswith('diversion')] assert len(output) == 1 line = output[0] pre, pkg_file = line.split(': ') packages = pre.split(', ') packages = [i for i in packages if i != pkg_from] assert len(packages) == 1 return packages[0] def _get_package(self): full_name = os.path.normpath(os.path.abspath(self.filename)) d = { '[': '\\[', ']': '\\]' } for i in d: full_name = full_name.replace(i, d[i]) output = safe_popen(['dpkg', '-S', full_name]) if output.startswith('diversion'): return self._parse_diversion(output) output = output.split('\n') output = [line for line in output if line] output = [i.split(': ', 1) for i in output] if len(output) != 1: print output raise RuntimeError("Error: file in more then 1 pkg? " + repr(output)) return output[0][0] def _get_needed(self): output = safe_popen(["objdump", "-x", self.filename]) output = output.split('\n') output = [i[len(' NEEDED'):].strip() for i in output if i.startswith(' NEEDED')] self.needed = output def _make_libs(self): libs = {} for soname in self.needed: libs[soname] = Library(soname, self.lib_search) self.libs = libs def _get_reloc(self): output = safe_popen(["objdump", "-R", self.filename]) output = output.split('\n') output = [i for i in output if i and i[0] in "0123456789abcdef"] output = [i.split()[2] for i in output] self.reloc = output def check_reloc(self): result = [soname for soname in self.needed] for symbol in self.reloc: found = False for soname in self.libs: if symbol in self.libs[soname].symbols_dict: if soname in result: result.remove(soname) found = True break if not found and not symbol in self._ignore_syms: #set_warning() # print >> sys.stderr, "Warning: Symbol not found:", symbol pass return result def is_warning(bin): if bin.warning: return bin.warning for i in bin.libs.values(): if i.warning: return i.warning return False def check_elf(filename): try: st = file(filename).read(4) except IOError: return False if st != '\x7fELF': return False return True def main(): p = pkg_tools.Packages('files/Packages.unstable') for file in sys.argv[1:]: if not check_elf(file): continue try: sys.stdout.write(file + ': ') bin = Binary(file, p) res = bin.check_reloc() except Exception, value: if isinstance(value, KeyboardInterrupt): raise print value else: if is_warning(bin): sys.stdout.write('*** ') print ' '.join(res) sys.stdout.flush() sys.stderr.flush() if __name__ == '__main__': main()