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

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: remove unneeded make_rules.py change Created 8 years, 3 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 subprocess 13 import subprocess
14 import sys 14 import sys
15 import urllib 15 import urllib
16 import struct
binji 2012/09/24 19:37:04 alphabetize
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):
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 = None
103 self.ReadElfHeader()
104
105 def ReadElfHeader(self):
binji 2012/09/24 19:37:04 make this private.
106 """Determine architecture of nexe by reading elf header."""
107 # From elf.h:
108 # typedef struct
109 # {
110 # unsigned char» e_ident[EI_NIDENT]; /* Magic number and other info */
binji 2012/09/24 19:37:04 kill tab
111 # Elf64_Half e_type; /* Object file type */
112 # Elf64_Half e_machine; /* Architecture */
113 # ...
114 # } Elf32_Ehdr;
115 elf_header_format = '16s2H'
116 elf_header_size = struct.calcsize(elf_header_format)
117
118 with open(self.path, 'rb') as f:
119 header = f.read(elf_header_size)
120
121 header = struct.unpack(elf_header_format, header)
122 e_ident, _, e_machine = header
123
124 elf_magic = '\x7fELF'
125 if e_ident[:4] != elf_magic:
126 raise Error("Not a valid NaCL executable: %s" % self.path)
127
128 e_machine_mapping = {
129 3 : 'x86-32',
130 40 : 'arm',
131 62 : 'x86-64'
132 }
133 if e_machine not in e_machine_mapping:
134 raise Error("Unknown machine type: %s" % e_machine)
135
136 # set arch based on the machine type in the elf header
137 self.arch = e_machine_mapping[e_machine]
102 138
103 def __repr__(self): 139 def __repr__(self):
104 return "<ArchFile %s>" % self.path 140 return "<ArchFile %s>" % self.path
105 141
106 def __str__(self): 142 def __str__(self):
107 '''Return the file path when invoked with the str() function''' 143 '''Return the file path when invoked with the str() function'''
108 return self.path 144 return self.path
109 145
110 146
111 class NmfUtils(object): 147 class NmfUtils(object):
112 '''Helper class for creating and managing nmf files 148 '''Helper class for creating and managing nmf files
113 149
114 Attributes: 150 Attributes:
115 manifest: A JSON-structured dict containing the nmf structure 151 manifest: A JSON-structured dict containing the nmf structure
116 needed: A dict with key=filename and value=ArchFile (see GetNeeded) 152 needed: A dict with key=filename and value=ArchFile (see GetNeeded)
117 ''' 153 '''
118 154
119 def __init__(self, main_files=None, objdump='x86_64-nacl-objdump', 155 def __init__(self, main_files=None, objdump=None,
120 lib_path=None, extra_files=None, lib_prefix=None, 156 lib_path=None, extra_files=None, lib_prefix=None,
121 toolchain=None, remap=None): 157 toolchain=None, remap=None):
122 '''Constructor 158 '''Constructor
123 159
124 Args: 160 Args:
125 main_files: List of main entry program files. These will be named 161 main_files: List of main entry program files. These will be named
126 files->main.nexe for dynamic nexes, and program for static nexes 162 files->main.nexe for dynamic nexes, and program for static nexes
127 objdump: path to x86_64-nacl-objdump tool (or Linux equivalent) 163 objdump: path to x86_64-nacl-objdump tool (or Linux equivalent)
128 lib_path: List of paths to library directories 164 lib_path: List of paths to library directories
129 extra_files: List of extra files to include in the nmf 165 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, 166 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. 167 both for staging the libraries and for inclusion into the nmf file.
132 Examples: ['..'], ['lib_dir'] 168 Examples: ['..'], ['lib_dir']
133 toolchain: Specify which toolchain newlib|glibc|pnacl which can require 169 toolchain: Specify which toolchain newlib|glibc|pnacl which can require
134 different forms of the NMF. 170 different forms of the NMF.
135 remap: Remaps the library name in the manifest. 171 remap: Remaps the library name in the manifest.
136 ''' 172 '''
137 self.objdump = objdump 173 self.objdump = objdump
138 self.main_files = main_files or [] 174 self.main_files = main_files or []
139 self.extra_files = extra_files or [] 175 self.extra_files = extra_files or []
140 self.lib_path = lib_path or [] 176 self.lib_path = lib_path or []
141 self.manifest = None 177 self.manifest = None
142 self.needed = None 178 self.needed = {}
143 self.lib_prefix = lib_prefix or [] 179 self.lib_prefix = lib_prefix or []
144 self.toolchain = toolchain 180 self.toolchain = toolchain
145 self.remap = remap or {} 181 self.remap = remap or {}
146 182
147 183
148 def GleanFromObjdump(self, files): 184 def GleanFromObjdump(self, files):
149 '''Get architecture and dependency information for given files 185 '''Get architecture and dependency information for given files
150 186
151 Args: 187 Args:
152 files: A dict with key=filename and value=list or set of archs. E.g.: 188 files: A dict with key=filename and value=list or set of archs. E.g.:
153 { '/path/to/my.nexe': ['x86-32'] 189 { '/path/to/my.nexe': ['x86-32']
154 '/path/to/lib64/libmy.so': ['x86-64'], 190 '/path/to/lib64/libmy.so': ['x86-64'],
155 '/path/to/mydata.so': ['x86-32', 'x86-64'], 191 '/path/to/mydata.so': ['x86-32', 'x86-64'],
156 '/path/to/my.data': None } # Indicates all architectures 192 '/path/to/my.data': None } # Indicates all architectures
157 193
158 Returns: A tuple with the following members: 194 Returns: A tuple with the following members:
159 input_info: A dict with key=filename and value=ArchFile of input files. 195 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. 196 Includes the input files as well, with arch filled in if absent.
161 Example: { '/path/to/my.nexe': ArchFile(my.nexe), 197 Example: { '/path/to/my.nexe': ArchFile(my.nexe),
162 '/path/to/libfoo.so': ArchFile(libfoo.so) } 198 '/path/to/libfoo.so': ArchFile(libfoo.so) }
163 needed: A set of strings formatted as "arch/name". Example: 199 needed: A set of strings formatted as "arch/name". Example:
164 set(['x86-32/libc.so', 'x86-64/libgcc.so']) 200 set(['x86-32/libc.so', 'x86-64/libgcc.so'])
165 ''' 201 '''
202 if not self.objdump:
203 raise Error("No objdump executable specified (see --help for more info)")
166 DebugPrint("GleanFromObjdump(%s)" % ([self.objdump, '-p'] + files.keys())) 204 DebugPrint("GleanFromObjdump(%s)" % ([self.objdump, '-p'] + files.keys()))
167 proc = subprocess.Popen([self.objdump, '-p'] + files.keys(), 205 proc = subprocess.Popen([self.objdump, '-p'] + files.keys(),
168 stdout=subprocess.PIPE, 206 stdout=subprocess.PIPE,
169 stderr=subprocess.PIPE, bufsize=-1) 207 stderr=subprocess.PIPE, bufsize=-1)
170 input_info = {} 208 input_info = {}
171 needed = set() 209 needed = set()
172 output, err_output = proc.communicate() 210 output, err_output = proc.communicate()
173 for line in output.splitlines(True): 211 for line in output.splitlines(True):
174 # Objdump should display the architecture first and then the dependencies 212 # Objdump should display the architecture first and then the dependencies
175 # second for each file in the list. 213 # second for each file in the list.
176 matched = FormatMatcher.match(line) 214 matched = FormatMatcher.match(line)
177 if matched is not None: 215 if matched is not None:
178 filename = matched.group(1) 216 filename = matched.group(1)
179 arch = FORMAT_ARCH_MAP[matched.group(2)] 217 arch = OBJDUMP_ARCH_MAP[matched.group(2)]
180 if files[filename] is None or arch in files[filename]: 218 if files[filename] is None or arch in files[filename]:
181 name = os.path.basename(filename) 219 name = os.path.basename(filename)
182 input_info[filename] = ArchFile( 220 input_info[filename] = ArchFile(
183 arch=arch,
184 name=name, 221 name=name,
185 path=filename, 222 path=filename,
186 url='/'.join(self.lib_prefix + [ARCH_LOCATION[arch], name])) 223 url='/'.join(self.lib_prefix + [ARCH_LOCATION[arch], name]))
187 matched = NeededMatcher.match(line) 224 matched = NeededMatcher.match(line)
188 if matched is not None: 225 if matched is not None:
189 if files[filename] is None or arch in files[filename]: 226 if files[filename] is None or arch in files[filename]:
190 needed.add('/'.join([arch, matched.group(1)])) 227 needed.add('/'.join([arch, matched.group(1)]))
191 status = proc.poll() 228 status = proc.poll()
192 if status != 0: 229 if status != 0:
193 raise Error('%s\nStdError=%s\nobjdump failed with error code: %d' % 230 raise Error('%s\nStdError=%s\nobjdump failed with error code: %d' %
(...skipping 23 matching lines...) Expand all
217 Returns: 254 Returns:
218 A dict with key=filename and value=ArchFile of input files. 255 A dict with key=filename and value=ArchFile of input files.
219 Includes the input files as well, with arch filled in if absent. 256 Includes the input files as well, with arch filled in if absent.
220 Example: { '/path/to/my.nexe': ArchFile(my.nexe), 257 Example: { '/path/to/my.nexe': ArchFile(my.nexe),
221 '/path/to/libfoo.so': ArchFile(libfoo.so) }''' 258 '/path/to/libfoo.so': ArchFile(libfoo.so) }'''
222 if self.needed: 259 if self.needed:
223 return self.needed 260 return self.needed
224 261
225 runnable = (self.toolchain != 'newlib' and self.toolchain != 'pnacl') 262 runnable = (self.toolchain != 'newlib' and self.toolchain != 'pnacl')
226 DebugPrint('GetNeeded(%s)' % self.main_files) 263 DebugPrint('GetNeeded(%s)' % self.main_files)
264
227 if runnable: 265 if runnable:
228 examined = set() 266 examined = set()
229 all_files, unexamined = self.GleanFromObjdump( 267 all_files, unexamined = self.GleanFromObjdump(
230 dict([(f, None) for f in self.main_files])) 268 dict([(f, None) for f in self.main_files]))
231 for name, arch_file in all_files.items(): 269 for name, arch_file in all_files.items():
232 arch_file.url = name 270 arch_file.url = name
233 if unexamined: 271 if unexamined:
234 unexamined.add('/'.join([arch_file.arch, RUNNABLE_LD])) 272 unexamined.add('/'.join([arch_file.arch, RUNNABLE_LD]))
235 while unexamined: 273 while unexamined:
236 files_to_examine = {} 274 files_to_examine = {}
237 for arch_name in unexamined: 275 for arch_name in unexamined:
238 arch, name = arch_name.split('/') 276 arch, name = arch_name.split('/')
239 for path in self.FindLibsInPath(name): 277 for path in self.FindLibsInPath(name):
240 files_to_examine.setdefault(path, set()).add(arch) 278 files_to_examine.setdefault(path, set()).add(arch)
241 new_files, needed = self.GleanFromObjdump(files_to_examine) 279 new_files, needed = self.GleanFromObjdump(files_to_examine)
242 all_files.update(new_files) 280 all_files.update(new_files)
243 examined |= unexamined 281 examined |= unexamined
244 unexamined = needed - examined 282 unexamined = needed - examined
283
245 # With the runnable-ld.so scheme we have today, the proper name of 284 # 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. 285 # 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())] 286 ldso = [LD_NACL_MAP[arch] for arch in set(OBJDUMP_ARCH_MAP.values())]
248 for name, arch_map in all_files.items(): 287 for name, arch_map in all_files.items():
249 if arch_map.name in ldso: 288 if arch_map.name in ldso:
250 del all_files[name] 289 del all_files[name]
290
251 self.needed = all_files 291 self.needed = all_files
252 else: 292 else:
253 need = {}
254 for filename in self.main_files: 293 for filename in self.main_files:
255 arch = filename.split('_')[-1]
256 arch = NAME_ARCH_MAP[arch]
257 url = os.path.split(filename)[1] 294 url = os.path.split(filename)[1]
258 need[filename] = ArchFile(arch=arch, name=os.path.basename(filename), 295 archfile = ArchFile(name=os.path.basename(filename),
259 path=filename, url=url) 296 path=filename, url=url)
260 self.needed = need 297 self.needed[filename] = archfile
261 298
262 return self.needed 299 return self.needed
263 300
264 def StageDependencies(self, destination_dir): 301 def StageDependencies(self, destination_dir):
265 '''Copies over the dependencies into a given destination directory 302 '''Copies over the dependencies into a given destination directory
266 303
267 Each library will be put into a subdirectory that corresponds to the arch. 304 Each library will be put into a subdirectory that corresponds to the arch.
268 305
269 Args: 306 Args:
270 destination_dir: The destination directory for staging the dependencies 307 destination_dir: The destination directory for staging the dependencies
(...skipping 92 matching lines...) Expand 10 before | Expand all | Expand 10 after
363 400
364 Trace.verbose = False 401 Trace.verbose = False
365 402
366 403
367 def Main(argv): 404 def Main(argv):
368 parser = optparse.OptionParser( 405 parser = optparse.OptionParser(
369 usage='Usage: %prog [options] nexe [extra_libs...]') 406 usage='Usage: %prog [options] nexe [extra_libs...]')
370 parser.add_option('-o', '--output', dest='output', 407 parser.add_option('-o', '--output', dest='output',
371 help='Write manifest file to FILE (default is stdout)', 408 help='Write manifest file to FILE (default is stdout)',
372 metavar='FILE') 409 metavar='FILE')
373 parser.add_option('-D', '--objdump', dest='objdump', default='objdump', 410 parser.add_option('-D', '--objdump', dest='objdump',
374 help='Use TOOL as the "objdump" tool to run', 411 help='Use TOOL as the "objdump" tool to run',
375 metavar='TOOL') 412 metavar='TOOL')
376 parser.add_option('-L', '--library-path', dest='lib_path', 413 parser.add_option('-L', '--library-path', dest='lib_path',
377 action='append', default=[], 414 action='append', default=[],
378 help='Add DIRECTORY to library search path', 415 help='Add DIRECTORY to library search path',
379 metavar='DIRECTORY') 416 metavar='DIRECTORY')
380 parser.add_option('-P', '--path-prefix', dest='path_prefix', default='', 417 parser.add_option('-P', '--path-prefix', dest='path_prefix', default='',
381 help='A path to prepend to shared libraries in the .nmf', 418 help='A path to prepend to shared libraries in the .nmf',
382 metavar='DIRECTORY') 419 metavar='DIRECTORY')
383 parser.add_option('-s', '--stage-dependencies', dest='stage_dependencies', 420 parser.add_option('-s', '--stage-dependencies', dest='stage_dependencies',
(...skipping 61 matching lines...) Expand 10 before | Expand all | Expand 10 after
445 482
446 483
447 # Invoke this file directly for simple testing. 484 # Invoke this file directly for simple testing.
448 if __name__ == '__main__': 485 if __name__ == '__main__':
449 try: 486 try:
450 rtn = Main(sys.argv[1:]) 487 rtn = Main(sys.argv[1:])
451 except Error, e: 488 except Error, e:
452 print "%s: %s" % (os.path.basename(__file__), e) 489 print "%s: %s" % (os.path.basename(__file__), e)
453 rtn = 1 490 rtn = 1
454 sys.exit(rtn) 491 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