| #!/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() |