Autofill: Add WalletIntegrationAvailable() to components.
[chromium-blink-merge.git] / native_client_sdk / src / tools / lib / get_shared_deps.py
blob7a355cadc2e569aa5f583c3d9d8f0467269c3ac3
1 # Copyright 2014 The Chromium Authors. All rights reserved.
2 # Use of this source code is governed by a BSD-style license that can be
3 # found in the LICENSE file.
5 """Helper script to close over all transitive dependencies of a given .nexe
6 executable.
8 e.g. Given
9 A -> B
10 B -> C
11 B -> D
12 C -> E
14 where "A -> B" means A depends on B, then GetNeeded(A) will return A, B, C, D
15 and E.
16 """
18 import os
19 import re
20 import subprocess
22 import elf
24 SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
25 SDK_DIR = os.path.dirname(os.path.dirname(SCRIPT_DIR))
27 NeededMatcher = re.compile('^ *NEEDED *([^ ]+)\n$')
28 FormatMatcher = re.compile('^(.+):\\s*file format (.+)\n$')
30 RUNNABLE_LD = 'runnable-ld.so' # Name of the dynamic loader
32 OBJDUMP_ARCH_MAP = {
33 # Names returned by Linux's objdump:
34 'elf64-x86-64': 'x86-64',
35 'elf32-i386': 'x86-32',
36 'elf32-little': 'arm',
37 'elf32-littlearm': 'arm',
38 # Names returned by old x86_64-nacl-objdump:
39 'elf64-nacl': 'x86-64',
40 'elf32-nacl': 'x86-32',
41 # Names returned by new x86_64-nacl-objdump:
42 'elf64-x86-64-nacl': 'x86-64',
43 'elf32-x86-64-nacl': 'x86-64',
44 'elf32-i386-nacl': 'x86-32',
45 'elf32-littlearm-nacl': 'arm',
48 # The proper name of the dynamic linker, as kept in the IRT. This is
49 # excluded from the nmf file by convention.
50 LD_NACL_MAP = {
51 'x86-32': 'ld-nacl-x86-32.so.1',
52 'x86-64': 'ld-nacl-x86-64.so.1',
53 'arm': None,
57 class Error(Exception):
58 '''Local Error class for this file.'''
59 pass
62 class NoObjdumpError(Error):
63 '''Error raised when objdump is needed but not found'''
64 pass
67 def GetNeeded(main_files, objdump, lib_path):
68 '''Collect the list of dependencies for the main_files
70 Args:
71 main_files: A list of files to find dependencies of.
72 objdump: Path to the objdump executable.
73 lib_path: A list of paths to search for shared libraries.
75 Returns:
76 A dict with key=filename and value=architecture. The architecture will be
77 one of ('x86_32', 'x86_64', 'arm').
78 '''
80 dynamic = any(elf.ParseElfHeader(f)[1] for f in main_files)
82 if dynamic:
83 return _GetNeededDynamic(main_files, objdump, lib_path)
84 else:
85 return _GetNeededStatic(main_files)
88 def _GetNeededDynamic(main_files, objdump, lib_path):
89 examined = set()
90 all_files, unexamined = GleanFromObjdump(main_files, None, objdump, lib_path)
91 for arch in all_files.itervalues():
92 if unexamined:
93 unexamined.add((RUNNABLE_LD, arch))
95 while unexamined:
96 files_to_examine = {}
98 # Take all the currently unexamined files and group them
99 # by architecture.
100 for name, arch in unexamined:
101 files_to_examine.setdefault(arch, []).append(name)
103 # Call GleanFromObjdump() for each architecture.
104 needed = set()
105 for arch, files in files_to_examine.iteritems():
106 new_files, new_needed = GleanFromObjdump(files, arch, objdump, lib_path)
107 all_files.update(new_files)
108 needed |= new_needed
110 examined |= unexamined
111 unexamined = needed - examined
113 # With the runnable-ld.so scheme we have today, the proper name of
114 # the dynamic linker should be excluded from the list of files.
115 ldso = [LD_NACL_MAP[arch] for arch in set(OBJDUMP_ARCH_MAP.values())]
116 for filename, arch in all_files.items():
117 name = os.path.basename(filename)
118 if name in ldso:
119 del all_files[filename]
121 return all_files
124 def GleanFromObjdump(files, arch, objdump, lib_path):
125 '''Get architecture and dependency information for given files
127 Args:
128 files: A list of files to examine.
129 [ '/path/to/my.nexe',
130 '/path/to/lib64/libmy.so',
131 '/path/to/mydata.so',
132 '/path/to/my.data' ]
133 arch: The architecure we are looking for, or None to accept any
134 architecture.
135 objdump: Path to the objdump executable.
136 lib_path: A list of paths to search for shared libraries.
138 Returns: A tuple with the following members:
139 input_info: A dict with key=filename and value=architecture. The
140 architecture will be one of ('x86_32', 'x86_64', 'arm').
141 needed: A set of strings formatted as "arch/name". Example:
142 set(['x86-32/libc.so', 'x86-64/libgcc.so'])
144 if not objdump:
145 raise NoObjdumpError('No objdump executable found!')
147 full_paths = set()
148 for filename in files:
149 if os.path.exists(filename):
150 full_paths.add(filename)
151 else:
152 for path in _FindLibsInPath(filename, lib_path):
153 full_paths.add(path)
155 cmd = [objdump, '-p'] + list(full_paths)
156 env = {'LANG': 'en_US.UTF-8'}
157 proc = subprocess.Popen(cmd, stdout=subprocess.PIPE,
158 stderr=subprocess.PIPE, bufsize=-1,
159 env=env)
161 input_info = {}
162 found_basenames = set()
163 needed = set()
164 output, err_output = proc.communicate()
165 if proc.returncode:
166 raise Error('%s\nStdError=%s\nobjdump failed with error code: %d' %
167 (output, err_output, proc.returncode))
169 file_arch = None
170 for line in output.splitlines(True):
171 # Objdump should display the architecture first and then the dependencies
172 # second for each file in the list.
173 matched = FormatMatcher.match(line)
174 if matched:
175 filename = matched.group(1)
176 file_arch = OBJDUMP_ARCH_MAP[matched.group(2)]
177 if arch and file_arch != arch:
178 continue
179 name = os.path.basename(filename)
180 found_basenames.add(name)
181 input_info[filename] = file_arch
182 matched = NeededMatcher.match(line)
183 if matched:
184 if arch and file_arch != arch:
185 continue
186 filename = matched.group(1)
187 new_needed = (filename, file_arch)
188 needed.add(new_needed)
190 for filename in files:
191 if os.path.basename(filename) not in found_basenames:
192 raise Error('Library not found [%s]: %s' % (arch, filename))
194 return input_info, needed
197 def _FindLibsInPath(name, lib_path):
198 '''Finds the set of libraries matching |name| within lib_path
200 Args:
201 name: name of library to find
202 lib_path: A list of paths to search for shared libraries.
204 Returns:
205 A list of system paths that match the given name within the lib_path'''
206 files = []
207 for dirname in lib_path:
208 # The libc.so files in the the glibc toolchain is actually a linker
209 # script which references libc.so.<SHA1>. This means the lib.so itself
210 # does not end up in the NEEDED section for glibc. However with bionic
211 # the SONAME is actually libc.so. If we pass glibc's libc.so to objdump
212 # if fails to parse it, os this filters out libc.so expept for within
213 # the bionic toolchain.
214 # TODO(bradnelson): Remove this once the SONAME in bionic is made to be
215 # unique in the same it is under glibc:
216 # https://code.google.com/p/nativeclient/issues/detail?id=3833
217 rel_dirname = os.path.relpath(dirname, SDK_DIR)
218 if name == 'libc.so' and 'bionic' not in rel_dirname:
219 continue
220 filename = os.path.join(dirname, name)
221 if os.path.exists(filename):
222 files.append(filename)
223 if not files:
224 raise Error('cannot find library %s' % name)
225 return files
228 def _GetNeededStatic(main_files):
229 needed = {}
230 for filename in main_files:
231 arch = elf.ParseElfHeader(filename)[0]
232 needed[filename] = arch
233 return needed