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