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

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

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