mirror of
				https://github.com/flatcar/scripts.git
				synced 2025-10-26 05:41:11 +01:00 
			
		
		
		
	check_deps checks for libraries using RPATH, but does not take the
similar RUNPATH into account.  For the purposes of chasing down
library dependencies, finding a library on either path suffices.
BUG=None
TEST=Build an image that includes binaries and associated libraries in a
     non-standard location, with the binaries and libraries setting an
     RUNPATH (and not an RPATH) pointing at that non-standard location.
Change-Id: Ic930bbacbe5c8ddeb367c39960dadea8aaba0cb2
Reviewed-on: https://gerrit.chromium.org/gerrit/39397
Tested-by: Josh Triplett <josh@joshtriplett.org>
Reviewed-by: Mike Frysinger <vapier@chromium.org>
Commit-Ready: Josh Triplett <josh@joshtriplett.org>
		
	
			
		
			
				
	
	
		
			191 lines
		
	
	
		
			5.1 KiB
		
	
	
	
		
			Python
		
	
	
		
			Executable File
		
	
	
	
	
			
		
		
	
	
			191 lines
		
	
	
		
			5.1 KiB
		
	
	
	
		
			Python
		
	
	
		
			Executable File
		
	
	
	
	
| #!/usr/bin/python
 | |
| 
 | |
| # Copyright (c) 2010 The Chromium OS Authors. All rights reserved.
 | |
| # Use of this source code is governed by a BSD-style license that can be
 | |
| # found in the LICENSE file.
 | |
| 
 | |
| import os
 | |
| import re
 | |
| import sys
 | |
| import glob
 | |
| 
 | |
| _SHARED_RE = re.compile(r"Shared library: \[([^\]]+)\]")
 | |
| _RPATH_RE = re.compile(r"Library r(?:un)?path: \[([^\]]+)\]")
 | |
| 
 | |
| 
 | |
| class CheckDependencies(object):
 | |
|   """Check that dependencies for binaries can be found in the specified dir."""
 | |
| 
 | |
|   def _ReadLdSoConf(self, path):
 | |
|     """Parse ld.so.conf files.
 | |
| 
 | |
|     Starting with the file at PATH (searched relative to self._root), return
 | |
|     all the valid libdirs found. Include directives are handled recursively.
 | |
| 
 | |
|     Args:
 | |
|       path: the path to the ld.so.conf file (inside of the root).
 | |
| 
 | |
|     Returns:
 | |
|       A list of valid libdirs.
 | |
|     """
 | |
| 
 | |
|     libdirs = []
 | |
| 
 | |
|     ld_so_conf = self._root + path
 | |
|     if os.path.exists(ld_so_conf):
 | |
|       f = file(ld_so_conf)
 | |
| 
 | |
|       for line in f:
 | |
|         line = line.rstrip()
 | |
| 
 | |
|         if line.startswith("/"):
 | |
|           libpath = self._root + line
 | |
|           if os.path.exists(libpath):
 | |
|             libdirs.append(libpath)
 | |
| 
 | |
|         elif line.startswith("include "):
 | |
|           # Includes are absolute or relative to the file itself.
 | |
|           line = os.path.join(os.path.dirname(path), line[8:])
 | |
|           for p in glob.glob(self._root + line):
 | |
|             libdirs.extend(self._ReadLdSoConf(os.path.relpath(p, self._root)))
 | |
| 
 | |
|       f.close()
 | |
| 
 | |
|     return libdirs
 | |
| 
 | |
|   def __init__(self, root, verbose=False):
 | |
|     """Initializer.
 | |
| 
 | |
|     Args:
 | |
|       root: The sysroot (e.g. "/")
 | |
|       verbose: Print helpful messages.
 | |
|     """
 | |
| 
 | |
|     self._root = root
 | |
|     self._libcache = set()
 | |
|     self._verbose = verbose
 | |
| 
 | |
|     libdirs = []
 | |
| 
 | |
|     # Add the native paths. Get the ELF interpreter from a known file
 | |
|     # and assume that the path it is in is our native libdir. So here
 | |
|     # we would get something like "/lib64/ld-linux-x86-64.so.2".
 | |
|     elf = "/bin/sh"
 | |
|     f = os.popen("scanelf -qF'%%i#p' %s/%s" % (root, elf))
 | |
|     native_libdir = os.path.dirname(f.readline().rstrip())
 | |
|     f.close()
 | |
|     if len(native_libdir) == 0:
 | |
|       print >>sys.stderr, "Problem with %s: can't find ELF interp" % elf
 | |
|       sys.exit(1)
 | |
|     elif native_libdir != "/lib":
 | |
|       libdirs.extend([
 | |
|         "%s/%s" % (root, native_libdir),
 | |
|         "%s/usr%s" % (root, native_libdir)
 | |
|       ])
 | |
| 
 | |
|     # Insert some default directories into our library cache.
 | |
|     libdirs.extend([
 | |
|       "%s/lib" % root,
 | |
|       "%s/usr/lib" % root,
 | |
|       "%s/opt/google/o3d/lib" % root,
 | |
|       "%s/usr/lib/opengl/xorg-x11/lib" % root,
 | |
|       "%s/usr/local/lib/icedtea6/jre/lib/i386/client" % root,
 | |
|       "%s/usr/local/lib/icedtea6/jre/lib/i386/headless" % root
 | |
|     ])
 | |
| 
 | |
|     # Read more directories from ld.so.conf.
 | |
|     libdirs.extend(self._ReadLdSoConf("/etc/ld.so.conf"))
 | |
| 
 | |
|     self._ReadLibs(libdirs, self._libcache)
 | |
| 
 | |
|   def _ReadLibs(self, paths, libcache):
 | |
|     for path in paths:
 | |
|       if os.path.exists(path):
 | |
|         for lib in os.listdir(path):
 | |
|           libcache.add(lib)
 | |
| 
 | |
|   def _ReadDependencies(self, binary):
 | |
|     """Run readelf -d on BINARY, returning (deps, rpaths)."""
 | |
| 
 | |
|     deps = set()
 | |
|     rpaths = set()
 | |
| 
 | |
|     # Read list of dynamic libraries, ignoring error messages that occur
 | |
|     # when we look at files that aren't actually libraries
 | |
|     f = os.popen("readelf -d '%s' 2>/dev/null" % binary)
 | |
|     for line in f:
 | |
| 
 | |
|       # Grab dependencies
 | |
|       m = _SHARED_RE.search(line)
 | |
|       if m:
 | |
|         deps.add(m.group(1))
 | |
| 
 | |
|       # Add RPATHs in our search path
 | |
|       m = _RPATH_RE.search(line)
 | |
|       if m:
 | |
|         for path in m.group(1).split(":"):
 | |
|           if path.startswith("$ORIGIN"):
 | |
|             rpaths.add(path.replace("$ORIGIN", os.path.dirname(binary)))
 | |
|           else:
 | |
|             rpaths.add(os.path.join(self._root, path[1:]))
 | |
|     f.close()
 | |
| 
 | |
|     return (deps, rpaths)
 | |
| 
 | |
|   def CheckDependencies(self, binary):
 | |
|     """Check whether the libs for BINARY can be found in our sysroot."""
 | |
| 
 | |
|     good = True
 | |
| 
 | |
|     deps, rpaths = self._ReadDependencies(binary)
 | |
| 
 | |
|     if self._verbose:
 | |
|       for lib in self._libcache & deps:
 | |
|         print "Found %s" % lib
 | |
| 
 | |
|     for lib in deps - self._libcache:
 | |
|       if lib[0] != "/":
 | |
|         for path in rpaths:
 | |
|           if os.path.exists(os.path.join(path, lib)):
 | |
|             if self._verbose:
 | |
|               print "Found %s" % lib
 | |
|             break
 | |
|         else:
 | |
|           print >>sys.stderr, "Problem with %s: Can't find %s" % (binary, lib)
 | |
|           good = False
 | |
|       else:
 | |
|         full_path = os.path.join(self._root, lib[1:])
 | |
|         if os.path.exists(full_path):
 | |
|           if self._verbose: print "Found %s" % lib
 | |
|         else:
 | |
|           print >>sys.stderr, "Problem with %s: Can't find %s" % (binary, lib)
 | |
|           good = False
 | |
| 
 | |
|     return good
 | |
| 
 | |
| 
 | |
| def main():
 | |
|   if len(sys.argv) < 3:
 | |
|     print "Usage: %s [-v] sysroot binary [ binary ... ]" % sys.argv[0]
 | |
|     sys.exit(1)
 | |
| 
 | |
|   verbose = False
 | |
|   if sys.argv[1] == "-v":
 | |
|     verbose = True
 | |
|     sys.argv = sys.argv[0:1] + sys.argv[2:]
 | |
| 
 | |
|   checker = CheckDependencies(sys.argv[1], verbose)
 | |
|   errors = False
 | |
|   for binary in sys.argv[2:]:
 | |
|     if verbose: print "Checking %s" % binary
 | |
|     if not checker.CheckDependencies(binary):
 | |
|       errors = True
 | |
| 
 | |
|   if errors:
 | |
|     sys.exit(1)
 | |
|   else:
 | |
|     sys.exit(0)
 | |
| 
 | |
| if __name__ == "__main__":
 | |
|   main()
 |