Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(45)

Side by Side Diff: native_client_sdk/src/tools/create_nmf.py

Issue 10979011: Revert 158451 - Always use objdump to determine arch of nexe (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src/
Patch Set: Created 8 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « no previous file | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 #!/usr/bin/env python 1 #!/usr/bin/env python
2 # Copyright (c) 2012 The Chromium Authors. All rights reserved. 2 # Copyright (c) 2012 The Chromium Authors. All rights reserved.
3 # Use of this source code is governed by a BSD-style license that can be 3 # Use of this source code is governed by a BSD-style license that can be
4 # found in the LICENSE file. 4 # found in the LICENSE file.
5 5
6 from __future__ import with_statement 6 from __future__ import with_statement
7 7
8 import errno 8 import errno
9 import optparse 9 import optparse
10 import os 10 import os
11 import re 11 import re
12 import shutil 12 import shutil
13 import struct
14 import subprocess 13 import subprocess
15 import sys 14 import sys
16 import urllib 15 import urllib
17 16
18 try: 17 try:
19 import json 18 import json
20 except ImportError: 19 except ImportError:
21 import simplejson as json 20 import simplejson as json
22 21
23 NeededMatcher = re.compile('^ *NEEDED *([^ ]+)\n$') 22 NeededMatcher = re.compile('^ *NEEDED *([^ ]+)\n$')
24 FormatMatcher = re.compile('^(.+):\\s*file format (.+)\n$') 23 FormatMatcher = re.compile('^(.+):\\s*file format (.+)\n$')
25 24
26 OBJDUMP_ARCH_MAP = { 25 FORMAT_ARCH_MAP = {
27 # Names returned by Linux's objdump: 26 # Names returned by Linux's objdump:
28 'elf64-x86-64': 'x86-64', 27 'elf64-x86-64': 'x86-64',
29 'elf32-i386': 'x86-32', 28 'elf32-i386': 'x86-32',
30 'elf32-little': 'arm',
31 'elf32-littlearm': 'arm',
32 # Names returned by x86_64-nacl-objdump: 29 # Names returned by x86_64-nacl-objdump:
33 'elf64-nacl': 'x86-64', 30 'elf64-nacl': 'x86-64',
34 'elf32-nacl': 'x86-32', 31 'elf32-nacl': 'x86-32',
35 } 32 }
36 33
37 ARCH_LOCATION = { 34 ARCH_LOCATION = {
38 'x86-32': 'lib32', 35 'x86-32': 'lib32',
39 'x86-64': 'lib64', 36 'x86-64': 'lib64',
40 'arm': 'lib',
41 } 37 }
42 38
39 NAME_ARCH_MAP = {
40 '32.nexe': 'x86-32',
41 '64.nexe': 'x86-64',
42 'arm.nexe': 'arm'
43 }
43 44
44 # These constants are used within nmf files. 45 # These constants are used within nmf files.
45 RUNNABLE_LD = 'runnable-ld.so' # Name of the dynamic loader 46 RUNNABLE_LD = 'runnable-ld.so' # Name of the dynamic loader
46 MAIN_NEXE = 'main.nexe' # Name of entry point for execution 47 MAIN_NEXE = 'main.nexe' # Name of entry point for execution
47 PROGRAM_KEY = 'program' # Key of the program section in an nmf file 48 PROGRAM_KEY = 'program' # Key of the program section in an nmf file
48 URL_KEY = 'url' # Key of the url field for a particular file in an nmf file 49 URL_KEY = 'url' # Key of the url field for a particular file in an nmf file
49 FILES_KEY = 'files' # Key of the files section in an nmf file 50 FILES_KEY = 'files' # Key of the files section in an nmf file
50 51
51 # The proper name of the dynamic linker, as kept in the IRT. This is 52 # The proper name of the dynamic linker, as kept in the IRT. This is
52 # excluded from the nmf file by convention. 53 # excluded from the nmf file by convention.
53 LD_NACL_MAP = { 54 LD_NACL_MAP = {
54 'x86-32': 'ld-nacl-x86-32.so.1', 55 'x86-32': 'ld-nacl-x86-32.so.1',
55 'x86-64': 'ld-nacl-x86-64.so.1', 56 'x86-64': 'ld-nacl-x86-64.so.1',
56 'arm': None,
57 } 57 }
58 58
59 59
60 def DebugPrint(message): 60 def DebugPrint(message):
61 if DebugPrint.debug_mode: 61 if DebugPrint.debug_mode:
62 sys.stderr.write('%s\n' % message) 62 sys.stderr.write('%s\n' % message)
63 63
64 64
65 DebugPrint.debug_mode = False # Set to True to enable extra debug prints 65 DebugPrint.debug_mode = False # Set to True to enable extra debug prints
66 66
(...skipping 15 matching lines...) Expand all
82 82
83 class Error(Exception): 83 class Error(Exception):
84 '''Local Error class for this file.''' 84 '''Local Error class for this file.'''
85 pass 85 pass
86 86
87 87
88 class ArchFile(object): 88 class ArchFile(object):
89 '''Simple structure containing information about 89 '''Simple structure containing information about
90 90
91 Attributes: 91 Attributes:
92 name: Name of this file 92 arch: Architecture of this file (e.g., x86-32)
93 filename: name of this file
93 path: Full path to this file on the build system 94 path: Full path to this file on the build system
94 arch: Architecture of this file (e.g., x86-32)
95 url: Relative path to file in the staged web directory. 95 url: Relative path to file in the staged web directory.
96 Used for specifying the "url" attribute in the nmf file.''' 96 Used for specifying the "url" attribute in the nmf file.'''
97 97 def __init__(self, arch, name, path='', url=None):
98 def __init__(self, name, path, url, arch=None): 98 self.arch = arch
99 self.name = name 99 self.name = name
100 self.path = path 100 self.path = path
101 self.url = url 101 self.url = url or '/'.join([arch, name])
102 self.arch = arch
103 if arch is None:
104 self._ReadElfHeader()
105
106 def _ReadElfHeader(self):
107 """Determine architecture of nexe by reading elf header."""
108 # From elf.h:
109 # typedef struct
110 # {
111 # unsigned char e_ident[EI_NIDENT]; /* Magic number and other info */
112 # Elf64_Half e_type; /* Object file type */
113 # Elf64_Half e_machine; /* Architecture */
114 # ...
115 # } Elf32_Ehdr;
116 elf_header_format = '16s2H'
117 elf_header_size = struct.calcsize(elf_header_format)
118
119 with open(self.path, 'rb') as f:
120 header = f.read(elf_header_size)
121
122 header = struct.unpack(elf_header_format, header)
123 e_ident, _, e_machine = header
124
125 elf_magic = '\x7fELF'
126 if e_ident[:4] != elf_magic:
127 raise Error("Not a valid NaCL executable: %s" % self.path)
128
129 e_machine_mapping = {
130 3 : 'x86-32',
131 40 : 'arm',
132 62 : 'x86-64'
133 }
134 if e_machine not in e_machine_mapping:
135 raise Error("Unknown machine type: %s" % e_machine)
136
137 # set arch based on the machine type in the elf header
138 self.arch = e_machine_mapping[e_machine]
139 102
140 def __repr__(self): 103 def __repr__(self):
141 return "<ArchFile %s>" % self.path 104 return "<ArchFile %s>" % self.path
142 105
143 def __str__(self): 106 def __str__(self):
144 '''Return the file path when invoked with the str() function''' 107 '''Return the file path when invoked with the str() function'''
145 return self.path 108 return self.path
146 109
147 110
148 class NmfUtils(object): 111 class NmfUtils(object):
149 '''Helper class for creating and managing nmf files 112 '''Helper class for creating and managing nmf files
150 113
151 Attributes: 114 Attributes:
152 manifest: A JSON-structured dict containing the nmf structure 115 manifest: A JSON-structured dict containing the nmf structure
153 needed: A dict with key=filename and value=ArchFile (see GetNeeded) 116 needed: A dict with key=filename and value=ArchFile (see GetNeeded)
154 ''' 117 '''
155 118
156 def __init__(self, main_files=None, objdump=None, 119 def __init__(self, main_files=None, objdump='x86_64-nacl-objdump',
157 lib_path=None, extra_files=None, lib_prefix=None, 120 lib_path=None, extra_files=None, lib_prefix=None,
158 toolchain=None, remap=None): 121 toolchain=None, remap=None):
159 '''Constructor 122 '''Constructor
160 123
161 Args: 124 Args:
162 main_files: List of main entry program files. These will be named 125 main_files: List of main entry program files. These will be named
163 files->main.nexe for dynamic nexes, and program for static nexes 126 files->main.nexe for dynamic nexes, and program for static nexes
164 objdump: path to x86_64-nacl-objdump tool (or Linux equivalent) 127 objdump: path to x86_64-nacl-objdump tool (or Linux equivalent)
165 lib_path: List of paths to library directories 128 lib_path: List of paths to library directories
166 extra_files: List of extra files to include in the nmf 129 extra_files: List of extra files to include in the nmf
167 lib_prefix: A list of path components to prepend to the library paths, 130 lib_prefix: A list of path components to prepend to the library paths,
168 both for staging the libraries and for inclusion into the nmf file. 131 both for staging the libraries and for inclusion into the nmf file.
169 Examples: ['..'], ['lib_dir'] 132 Examples: ['..'], ['lib_dir']
170 toolchain: Specify which toolchain newlib|glibc|pnacl which can require 133 toolchain: Specify which toolchain newlib|glibc|pnacl which can require
171 different forms of the NMF. 134 different forms of the NMF.
172 remap: Remaps the library name in the manifest. 135 remap: Remaps the library name in the manifest.
173 ''' 136 '''
174 self.objdump = objdump 137 self.objdump = objdump
175 self.main_files = main_files or [] 138 self.main_files = main_files or []
176 self.extra_files = extra_files or [] 139 self.extra_files = extra_files or []
177 self.lib_path = lib_path or [] 140 self.lib_path = lib_path or []
178 self.manifest = None 141 self.manifest = None
179 self.needed = {} 142 self.needed = None
180 self.lib_prefix = lib_prefix or [] 143 self.lib_prefix = lib_prefix or []
181 self.toolchain = toolchain 144 self.toolchain = toolchain
182 self.remap = remap or {} 145 self.remap = remap or {}
183 146
184 147
185 def GleanFromObjdump(self, files): 148 def GleanFromObjdump(self, files):
186 '''Get architecture and dependency information for given files 149 '''Get architecture and dependency information for given files
187 150
188 Args: 151 Args:
189 files: A dict with key=filename and value=list or set of archs. E.g.: 152 files: A dict with key=filename and value=list or set of archs. E.g.:
190 { '/path/to/my.nexe': ['x86-32'] 153 { '/path/to/my.nexe': ['x86-32']
191 '/path/to/lib64/libmy.so': ['x86-64'], 154 '/path/to/lib64/libmy.so': ['x86-64'],
192 '/path/to/mydata.so': ['x86-32', 'x86-64'], 155 '/path/to/mydata.so': ['x86-32', 'x86-64'],
193 '/path/to/my.data': None } # Indicates all architectures 156 '/path/to/my.data': None } # Indicates all architectures
194 157
195 Returns: A tuple with the following members: 158 Returns: A tuple with the following members:
196 input_info: A dict with key=filename and value=ArchFile of input files. 159 input_info: A dict with key=filename and value=ArchFile of input files.
197 Includes the input files as well, with arch filled in if absent. 160 Includes the input files as well, with arch filled in if absent.
198 Example: { '/path/to/my.nexe': ArchFile(my.nexe), 161 Example: { '/path/to/my.nexe': ArchFile(my.nexe),
199 '/path/to/libfoo.so': ArchFile(libfoo.so) } 162 '/path/to/libfoo.so': ArchFile(libfoo.so) }
200 needed: A set of strings formatted as "arch/name". Example: 163 needed: A set of strings formatted as "arch/name". Example:
201 set(['x86-32/libc.so', 'x86-64/libgcc.so']) 164 set(['x86-32/libc.so', 'x86-64/libgcc.so'])
202 ''' 165 '''
203 if not self.objdump:
204 raise Error("No objdump executable specified (see --help for more info)")
205 DebugPrint("GleanFromObjdump(%s)" % ([self.objdump, '-p'] + files.keys())) 166 DebugPrint("GleanFromObjdump(%s)" % ([self.objdump, '-p'] + files.keys()))
206 proc = subprocess.Popen([self.objdump, '-p'] + files.keys(), 167 proc = subprocess.Popen([self.objdump, '-p'] + files.keys(),
207 stdout=subprocess.PIPE, 168 stdout=subprocess.PIPE,
208 stderr=subprocess.PIPE, bufsize=-1) 169 stderr=subprocess.PIPE, bufsize=-1)
209 input_info = {} 170 input_info = {}
210 needed = set() 171 needed = set()
211 output, err_output = proc.communicate() 172 output, err_output = proc.communicate()
212 for line in output.splitlines(True): 173 for line in output.splitlines(True):
213 # Objdump should display the architecture first and then the dependencies 174 # Objdump should display the architecture first and then the dependencies
214 # second for each file in the list. 175 # second for each file in the list.
215 matched = FormatMatcher.match(line) 176 matched = FormatMatcher.match(line)
216 if matched is not None: 177 if matched is not None:
217 filename = matched.group(1) 178 filename = matched.group(1)
218 arch = OBJDUMP_ARCH_MAP[matched.group(2)] 179 arch = FORMAT_ARCH_MAP[matched.group(2)]
219 if files[filename] is None or arch in files[filename]: 180 if files[filename] is None or arch in files[filename]:
220 name = os.path.basename(filename) 181 name = os.path.basename(filename)
221 input_info[filename] = ArchFile( 182 input_info[filename] = ArchFile(
222 arch=arch, 183 arch=arch,
223 name=name, 184 name=name,
224 path=filename, 185 path=filename,
225 url='/'.join(self.lib_prefix + [ARCH_LOCATION[arch], name])) 186 url='/'.join(self.lib_prefix + [ARCH_LOCATION[arch], name]))
226 matched = NeededMatcher.match(line) 187 matched = NeededMatcher.match(line)
227 if matched is not None: 188 if matched is not None:
228 if files[filename] is None or arch in files[filename]: 189 if files[filename] is None or arch in files[filename]:
(...skipping 27 matching lines...) Expand all
256 Returns: 217 Returns:
257 A dict with key=filename and value=ArchFile of input files. 218 A dict with key=filename and value=ArchFile of input files.
258 Includes the input files as well, with arch filled in if absent. 219 Includes the input files as well, with arch filled in if absent.
259 Example: { '/path/to/my.nexe': ArchFile(my.nexe), 220 Example: { '/path/to/my.nexe': ArchFile(my.nexe),
260 '/path/to/libfoo.so': ArchFile(libfoo.so) }''' 221 '/path/to/libfoo.so': ArchFile(libfoo.so) }'''
261 if self.needed: 222 if self.needed:
262 return self.needed 223 return self.needed
263 224
264 runnable = (self.toolchain != 'newlib' and self.toolchain != 'pnacl') 225 runnable = (self.toolchain != 'newlib' and self.toolchain != 'pnacl')
265 DebugPrint('GetNeeded(%s)' % self.main_files) 226 DebugPrint('GetNeeded(%s)' % self.main_files)
266
267 if runnable: 227 if runnable:
268 examined = set() 228 examined = set()
269 all_files, unexamined = self.GleanFromObjdump( 229 all_files, unexamined = self.GleanFromObjdump(
270 dict([(f, None) for f in self.main_files])) 230 dict([(f, None) for f in self.main_files]))
271 for name, arch_file in all_files.items(): 231 for name, arch_file in all_files.items():
272 arch_file.url = name 232 arch_file.url = name
273 if unexamined: 233 if unexamined:
274 unexamined.add('/'.join([arch_file.arch, RUNNABLE_LD])) 234 unexamined.add('/'.join([arch_file.arch, RUNNABLE_LD]))
275 while unexamined: 235 while unexamined:
276 files_to_examine = {} 236 files_to_examine = {}
277 for arch_name in unexamined: 237 for arch_name in unexamined:
278 arch, name = arch_name.split('/') 238 arch, name = arch_name.split('/')
279 for path in self.FindLibsInPath(name): 239 for path in self.FindLibsInPath(name):
280 files_to_examine.setdefault(path, set()).add(arch) 240 files_to_examine.setdefault(path, set()).add(arch)
281 new_files, needed = self.GleanFromObjdump(files_to_examine) 241 new_files, needed = self.GleanFromObjdump(files_to_examine)
282 all_files.update(new_files) 242 all_files.update(new_files)
283 examined |= unexamined 243 examined |= unexamined
284 unexamined = needed - examined 244 unexamined = needed - examined
285
286 # With the runnable-ld.so scheme we have today, the proper name of 245 # With the runnable-ld.so scheme we have today, the proper name of
287 # the dynamic linker should be excluded from the list of files. 246 # the dynamic linker should be excluded from the list of files.
288 ldso = [LD_NACL_MAP[arch] for arch in set(OBJDUMP_ARCH_MAP.values())] 247 ldso = [LD_NACL_MAP[arch] for arch in set(FORMAT_ARCH_MAP.values())]
289 for name, arch_map in all_files.items(): 248 for name, arch_map in all_files.items():
290 if arch_map.name in ldso: 249 if arch_map.name in ldso:
291 del all_files[name] 250 del all_files[name]
292
293 self.needed = all_files 251 self.needed = all_files
294 else: 252 else:
253 need = {}
295 for filename in self.main_files: 254 for filename in self.main_files:
255 arch = filename.split('_')[-1]
256 arch = NAME_ARCH_MAP[arch]
296 url = os.path.split(filename)[1] 257 url = os.path.split(filename)[1]
297 archfile = ArchFile(name=os.path.basename(filename), 258 need[filename] = ArchFile(arch=arch, name=os.path.basename(filename),
298 path=filename, url=url) 259 path=filename, url=url)
299 self.needed[filename] = archfile 260 self.needed = need
300 261
301 return self.needed 262 return self.needed
302 263
303 def StageDependencies(self, destination_dir): 264 def StageDependencies(self, destination_dir):
304 '''Copies over the dependencies into a given destination directory 265 '''Copies over the dependencies into a given destination directory
305 266
306 Each library will be put into a subdirectory that corresponds to the arch. 267 Each library will be put into a subdirectory that corresponds to the arch.
307 268
308 Args: 269 Args:
309 destination_dir: The destination directory for staging the dependencies 270 destination_dir: The destination directory for staging the dependencies
(...skipping 92 matching lines...) Expand 10 before | Expand all | Expand 10 after
402 363
403 Trace.verbose = False 364 Trace.verbose = False
404 365
405 366
406 def Main(argv): 367 def Main(argv):
407 parser = optparse.OptionParser( 368 parser = optparse.OptionParser(
408 usage='Usage: %prog [options] nexe [extra_libs...]') 369 usage='Usage: %prog [options] nexe [extra_libs...]')
409 parser.add_option('-o', '--output', dest='output', 370 parser.add_option('-o', '--output', dest='output',
410 help='Write manifest file to FILE (default is stdout)', 371 help='Write manifest file to FILE (default is stdout)',
411 metavar='FILE') 372 metavar='FILE')
412 parser.add_option('-D', '--objdump', dest='objdump', 373 parser.add_option('-D', '--objdump', dest='objdump', default='objdump',
413 help='Use TOOL as the "objdump" tool to run', 374 help='Use TOOL as the "objdump" tool to run',
414 metavar='TOOL') 375 metavar='TOOL')
415 parser.add_option('-L', '--library-path', dest='lib_path', 376 parser.add_option('-L', '--library-path', dest='lib_path',
416 action='append', default=[], 377 action='append', default=[],
417 help='Add DIRECTORY to library search path', 378 help='Add DIRECTORY to library search path',
418 metavar='DIRECTORY') 379 metavar='DIRECTORY')
419 parser.add_option('-P', '--path-prefix', dest='path_prefix', default='', 380 parser.add_option('-P', '--path-prefix', dest='path_prefix', default='',
420 help='A path to prepend to shared libraries in the .nmf', 381 help='A path to prepend to shared libraries in the .nmf',
421 metavar='DIRECTORY') 382 metavar='DIRECTORY')
422 parser.add_option('-s', '--stage-dependencies', dest='stage_dependencies', 383 parser.add_option('-s', '--stage-dependencies', dest='stage_dependencies',
(...skipping 61 matching lines...) Expand 10 before | Expand all | Expand 10 after
484 445
485 446
486 # Invoke this file directly for simple testing. 447 # Invoke this file directly for simple testing.
487 if __name__ == '__main__': 448 if __name__ == '__main__':
488 try: 449 try:
489 rtn = Main(sys.argv[1:]) 450 rtn = Main(sys.argv[1:])
490 except Error, e: 451 except Error, e:
491 print "%s: %s" % (os.path.basename(__file__), e) 452 print "%s: %s" % (os.path.basename(__file__), e)
492 rtn = 1 453 rtn = 1
493 sys.exit(rtn) 454 sys.exit(rtn)
OLDNEW
« no previous file with comments | « no previous file | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698