#!/usr/bin/env python3
import os
import re
from collections import defaultdict

from elftools.elf.elffile import ELFFile
from elftools.elf.dynamic import DynamicSection, DynamicSegment
from elftools.common.exceptions import ELFError
from elftools.common.py3compat import bytes2str

class BrokenFinder():

    def __init__(self):
        self.found = set()  # 'shared libraries' (could also be symlinks) that we found so far
        self.lib2required_by = defaultdict(list)

    def enumerate_shared_libs(self):
        somatching = re.compile(r""".*\.so\Z # normal shared object
        |.*\.so(\.\d+)+ # versioned shared object""", re.VERBOSE)
        for dpath, dnames, fnames in os.walk("/usr"):
            for fullname, fname in ((os.path.join(dpath, fname),fname) for fname in fnames if re.match(somatching ,fname)):
                self.found.add(fname)
                if not os.path.islink(fullname):
                    yield fullname

    def collect_needed(self, sofile):
        with open(sofile, 'rb') as f:
            try:
                elffile = ELFFile(f)
                for section in elffile.iter_sections():
                    if not isinstance(section, DynamicSection):
                        continue

                    for tag in section.iter_tags():
                        if tag.entry.d_tag == 'DT_NEEDED':
                            self.lib2required_by[bytes2str(tag.needed)].append(sofile)

            except ELFError:
                pass  # not an ELF file


    def check(self):
        for solib in self.enumerate_shared_libs():
            self.collect_needed(solib)
        missing_libs = self.lib2required_by.keys()  - self.found
        if missing_libs:
            print("==The following libraries were not found")
        for missing_lib in (missing_libs):
            print("{} required by: {}".format(missing_lib, ', '.join(self.lib2required_by[missing_lib])))

b = BrokenFinder()
b.check()

@@ -36,16 +36,43 @@
 port re%0A
+import itertools as itools%0A
 from col
@@ -492,16 +492,163 @@
 ct(list)
+%0A        # get all directories in PATH;  if unset, use %22/usr/bin%22 as a default%0A        self.bindirs = os.environ.get(%22PATH%22, %22/usr/bin%22).split(%22:%22)
 %0A%0A    de
@@ -1113,16 +1113,320 @@
 llname%0A%0A
+    def enumerate_binaries(self):%0A        for dpath, dnames, fnames in itools.chain(*(os.walk(bindir) for bindir in self.bindirs)):%0A            for fname in fnames:%0A                fullname = os.path.join(dpath, fname)%0A                if not os.path.islink(fullname):%0A                    yield fullname%0A%0A
     def 
@@ -1451,24 +1451,41 @@
 f, sofile):%0A
+        try:%0A    
         with
@@ -1510,16 +1510,20 @@
 ) as f:%0A
+    
         
@@ -1547,16 +1547,20 @@
         
+    
 elffile 
@@ -1572,16 +1572,20 @@
 File(f)%0A
+    
         
@@ -1648,24 +1648,28 @@
             
+    
 if not isins
@@ -1724,16 +1724,20 @@
         
+    
 continue
@@ -1754,24 +1754,28 @@
             
+ 
+   
 for tag in s
@@ -1790,24 +1790,28 @@
 ter_tags():%0A
+    
             
@@ -1877,32 +1877,36 @@
                 
+ 
+   
 self.lib2require
@@ -1961,16 +1961,20 @@
         
+    
 except E
@@ -1998,16 +1998,20 @@
         
+ 
+   
 pass  # 
@@ -2026,16 +2026,127 @@
 LF file%0A
+        except PermissionError:%0A            print(%22Could not open %7B%7D; please check permissions%22.format(sofile))
 %0A%0A    de
@@ -2176,17 +2176,35 @@
 for 
-so
 lib
- in 
+_or_bin in itools.chain(
 self
@@ -2227,16 +2227,44 @@
 d_libs()
+, self.enumerate_binaries())
 :%0A      
@@ -2293,13 +2293,18 @@
 ded(
-so
 lib
+_or_bin
 )%0A  

check binaries too