| OLD | NEW |
| 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 Loading... |
| 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 Loading... |
| 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 Loading... |
| 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 Loading... |
| 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) |
| OLD | NEW |