Wed, 31 Dec 2014 06:09:35 +0100
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() |