toolkit/library/dependentlibs.py

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

michael@0 1 # This Source Code Form is subject to the terms of the Mozilla Public
michael@0 2 # License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 3 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
michael@0 4
michael@0 5 '''Given a library, dependentlibs.py prints the list of libraries it depends
michael@0 6 upon that are in the same directory, followed by the library itself.
michael@0 7 '''
michael@0 8
michael@0 9 from optparse import OptionParser
michael@0 10 import os
michael@0 11 import re
michael@0 12 import fnmatch
michael@0 13 import subprocess
michael@0 14 import sys
michael@0 15 from mozpack.executables import (
michael@0 16 get_type,
michael@0 17 ELF,
michael@0 18 MACHO,
michael@0 19 )
michael@0 20
michael@0 21 TOOLCHAIN_PREFIX = ''
michael@0 22
michael@0 23 def dependentlibs_dumpbin(lib):
michael@0 24 '''Returns the list of dependencies declared in the given DLL'''
michael@0 25 try:
michael@0 26 proc = subprocess.Popen(['dumpbin', '-dependents', lib], stdout = subprocess.PIPE)
michael@0 27 except OSError:
michael@0 28 # dumpbin is missing, probably mingw compilation. Try using objdump.
michael@0 29 return dependentlibs_mingw_objdump(lib)
michael@0 30 deps = []
michael@0 31 for line in proc.stdout:
michael@0 32 # Each line containing an imported library name starts with 4 spaces
michael@0 33 match = re.match(' (\S+)', line)
michael@0 34 if match:
michael@0 35 deps.append(match.group(1))
michael@0 36 elif len(deps):
michael@0 37 # There may be several groups of library names, but only the
michael@0 38 # first one is interesting. The second one is for delayload-ed
michael@0 39 # libraries.
michael@0 40 break
michael@0 41 proc.wait()
michael@0 42 return deps
michael@0 43
michael@0 44 def dependentlibs_mingw_objdump(lib):
michael@0 45 proc = subprocess.Popen(['objdump', '-x', lib], stdout = subprocess.PIPE)
michael@0 46 deps = []
michael@0 47 for line in proc.stdout:
michael@0 48 match = re.match('\tDLL Name: (\S+)', line)
michael@0 49 if match:
michael@0 50 deps.append(match.group(1))
michael@0 51 proc.wait()
michael@0 52 return deps
michael@0 53
michael@0 54 def dependentlibs_readelf(lib):
michael@0 55 '''Returns the list of dependencies declared in the given ELF .so'''
michael@0 56 proc = subprocess.Popen([TOOLCHAIN_PREFIX + 'readelf', '-d', lib], stdout = subprocess.PIPE)
michael@0 57 deps = []
michael@0 58 for line in proc.stdout:
michael@0 59 # Each line has the following format:
michael@0 60 # tag (TYPE) value
michael@0 61 # Looking for NEEDED type entries
michael@0 62 tmp = line.split(' ', 3)
michael@0 63 if len(tmp) > 3 and tmp[2] == '(NEEDED)':
michael@0 64 # NEEDED lines look like:
michael@0 65 # 0x00000001 (NEEDED) Shared library: [libname]
michael@0 66 match = re.search('\[(.*)\]', tmp[3])
michael@0 67 if match:
michael@0 68 deps.append(match.group(1))
michael@0 69 proc.wait()
michael@0 70 return deps
michael@0 71
michael@0 72 def dependentlibs_otool(lib):
michael@0 73 '''Returns the list of dependencies declared in the given MACH-O dylib'''
michael@0 74 proc = subprocess.Popen(["../../../../../x-tools/x86_64-apple-darwin10/bin/" + TOOLCHAIN_PREFIX + 'otool', '-l', lib], stdout = subprocess.PIPE)
michael@0 75 deps= []
michael@0 76 cmd = None
michael@0 77 for line in proc.stdout:
michael@0 78 # otool -l output contains many different things. The interesting data
michael@0 79 # is under "Load command n" sections, with the content:
michael@0 80 # cmd LC_LOAD_DYLIB
michael@0 81 # cmdsize 56
michael@0 82 # name libname (offset 24)
michael@0 83 tmp = line.split()
michael@0 84 if len(tmp) < 2:
michael@0 85 continue
michael@0 86 if tmp[0] == 'cmd':
michael@0 87 cmd = tmp[1]
michael@0 88 elif cmd == 'LC_LOAD_DYLIB' and tmp[0] == 'name':
michael@0 89 deps.append(re.sub('^@executable_path/','',tmp[1]))
michael@0 90 proc.wait()
michael@0 91 return deps
michael@0 92
michael@0 93 def dependentlibs(lib, libpaths, func):
michael@0 94 '''For a given library, returns the list of recursive dependencies that can
michael@0 95 be found in the given list of paths, followed by the library itself.'''
michael@0 96 assert(libpaths)
michael@0 97 assert(isinstance(libpaths, list))
michael@0 98 deps = []
michael@0 99 for dep in func(lib):
michael@0 100 if dep in deps or os.path.isabs(dep):
michael@0 101 continue
michael@0 102 for dir in libpaths:
michael@0 103 deppath = os.path.join(dir, dep)
michael@0 104 if os.path.exists(deppath):
michael@0 105 deps.extend([d for d in dependentlibs(deppath, libpaths, func) if not d in deps])
michael@0 106 # Black list the ICU data DLL because preloading it at startup
michael@0 107 # leads to startup performance problems because of its excessive
michael@0 108 # size (around 10MB).
michael@0 109 if not dep.startswith("icu"):
michael@0 110 deps.append(dep)
michael@0 111 break
michael@0 112
michael@0 113 return deps
michael@0 114
michael@0 115 def main():
michael@0 116 parser = OptionParser()
michael@0 117 parser.add_option("-L", dest="libpaths", action="append", metavar="PATH", help="Add the given path to the library search path")
michael@0 118 parser.add_option("-p", dest="toolchain_prefix", metavar="PREFIX", help="Use the given prefix to readelf")
michael@0 119 (options, args) = parser.parse_args()
michael@0 120 if options.toolchain_prefix:
michael@0 121 global TOOLCHAIN_PREFIX
michael@0 122 TOOLCHAIN_PREFIX = options.toolchain_prefix
michael@0 123 lib = args[0]
michael@0 124 binary_type = get_type(lib)
michael@0 125 if binary_type == ELF:
michael@0 126 func = dependentlibs_readelf
michael@0 127 elif binary_type == MACHO:
michael@0 128 func = dependentlibs_otool
michael@0 129 else:
michael@0 130 ext = os.path.splitext(lib)[1]
michael@0 131 assert(ext == '.dll')
michael@0 132 func = dependentlibs_dumpbin
michael@0 133 if not options.libpaths:
michael@0 134 options.libpaths = [os.path.dirname(lib)]
michael@0 135
michael@0 136 print '\n'.join(dependentlibs(lib, options.libpaths, func) + [lib])
michael@0 137
michael@0 138 if __name__ == '__main__':
michael@0 139 main()

mercurial