Enable dev build with the latest repo
[platform/framework/web/chromium-efl.git] / native_client / run.py
1 #!/usr/bin/python
2 # Copyright (c) 2012 The Native Client Authors. All rights reserved.
3 # Use of this source code is governed by a BSD-style license that can be
4 # found in the LICENSE file.
5
6 from __future__ import print_function
7
8 import argparse
9 import os
10 import subprocess
11 import sys
12 import tempfile
13
14 import pynacl.platform
15
16 # Target architecture for PNaCl can be set through the ``-arch``
17 # command-line argument, and when its value is ``env`` the following
18 # program environment variable is queried to figure out which
19 # architecture to target.
20 ARCH_ENV_VAR_NAME = 'PNACL_RUN_ARCH'
21
22 class Environment:
23   pass
24 env = Environment()
25
26 def SetupEnvironment():
27   # native_client/ directory
28   env.nacl_root = FindBaseDir()
29
30   env.toolchain_base = os.path.join(env.nacl_root,
31                                     'toolchain',
32                                     '%s_x86' % pynacl.platform.GetOS())
33
34   # Path to PNaCl toolchain
35   env.pnacl_base = os.path.join(env.toolchain_base, 'pnacl_newlib')
36
37   # QEMU
38   env.arm_root = os.path.join(env.toolchain_base, 'arm_trusted')
39   env.qemu_arm = os.path.join(env.arm_root, 'run_under_qemu_arm')
40
41   env.mips32_root = os.path.join(env.toolchain_base, 'mips_trusted')
42   env.qemu_mips32 = os.path.join(env.mips32_root, 'run_under_qemu_mips32')
43
44   # Path to 'readelf'
45   env.readelf = FindReadElf()
46
47   # Path to 'scons'
48   env.scons = os.path.join(env.nacl_root, 'scons')
49
50   # Library path for dynamic linker.
51   env.library_path = []
52
53   # Suppress -S -a
54   env.paranoid = False
55
56   # Only print commands, don't run them
57   env.dry_run = False
58
59   # Force a specific sel_ldr
60   env.force_sel_ldr = None
61
62   # Force a specific IRT
63   env.force_irt = None
64
65   # Don't print anything
66   env.quiet = False
67
68   # Arch (x86-32, x86-64, arm, mips32)
69   env.arch = None
70
71   # Trace in QEMU
72   env.trace = False
73
74   # Debug the nexe using the debug stub
75   env.debug = False
76
77   # PNaCl (as opposed to NaCl).
78   env.is_pnacl = False
79
80 def PrintBanner(output):
81   if not env.quiet:
82     lines = output.split('\n')
83     print('*' * 80)
84     for line in lines:
85       padding = ' ' * max(0, (80 - len(line)) / 2)
86       print(padding + output + padding)
87     print('*' * 80)
88
89
90 def PrintCommand(s):
91   if not env.quiet:
92     print()
93     print(s)
94     print()
95
96
97 def SetupArch(arch, allow_build=True):
98   '''Setup environment variables that require knowing the
99      architecture. We can only do this after we've seen the
100      nexe or once we've read -arch off the command-line.
101   '''
102   env.arch = arch
103   # Path to Native NaCl toolchain (glibc)
104   # MIPS does not have glibc support.
105   if arch != 'mips32':
106     toolchain_arch, tooldir_arch, libdir = {
107         'x86-32': ('x86', 'x86_64', 'lib32'),
108         'x86-64': ('x86', 'x86_64', 'lib'),
109         'arm': ('arm', 'arm', 'lib'),
110         }[arch]
111     env.nnacl_tooldir = os.path.join(env.toolchain_base,
112                                      'nacl_%s_glibc' % toolchain_arch,
113                                      '%s-nacl' % tooldir_arch)
114     env.nnacl_libdir = os.path.join(env.nnacl_tooldir, libdir)
115   env.sel_ldr = FindOrBuildSelLdr(allow_build=allow_build)
116   env.irt = FindOrBuildIRT(allow_build=allow_build)
117   if arch == 'arm':
118     env.elf_loader = FindOrBuildElfLoader(allow_build=allow_build)
119
120
121 def SetupLibC(arch, is_dynamic):
122   if is_dynamic:
123     if env.is_pnacl:
124       libdir = os.path.join(env.pnacl_base, 'lib-' + arch)
125     else:
126       libdir = env.nnacl_libdir
127     env.runnable_ld = os.path.join(libdir, 'runnable-ld.so')
128     env.library_path.append(libdir)
129
130
131 def main(argv):
132   SetupEnvironment()
133   return_code = 0
134
135   sel_ldr_options = []
136   # sel_ldr's "quiet" options need to come early in the command line
137   # to suppress noisy output from processing other options, like -Q.
138   sel_ldr_quiet_options = []
139   nexe, nexe_params = ArgSplit(argv[1:])
140
141   try:
142     if env.is_pnacl:
143       nexe = Translate(env.arch, nexe)
144
145     # Read the ELF file info
146     if env.is_pnacl and env.dry_run:
147       # In a dry run, we don't actually run pnacl-translate, so there is
148       # no nexe for readelf. Fill in the information manually.
149       arch = env.arch
150       is_dynamic = False
151       is_glibc_static = False
152     else:
153       arch, is_dynamic, is_glibc_static = ReadELFInfo(nexe)
154
155     # Add default sel_ldr options
156     if not env.paranoid:
157       # Enable mmap() for loading the nexe, so that 'perf' gives usable output.
158       # Note that this makes the sandbox unsafe if the mmap()'d nexe gets
159       # modified while sel_ldr is running.
160       os.environ['NACL_FAULT_INJECTION'] = \
161         'ELF_LOAD_BYPASS_DESCRIPTOR_SAFETY_CHECK=GF1/999'
162       sel_ldr_options += ['-a']
163       # -S signal handling is not supported on windows, but otherwise
164       # it is useful getting the address of crashes.
165       if not pynacl.platform.IsWindows():
166         sel_ldr_options += ['-S']
167       # X86-64 glibc static has validation problems without stub out (-s)
168       if arch == 'x86-64' and is_glibc_static:
169         sel_ldr_options += ['-s']
170     if env.quiet:
171       # Don't print sel_ldr logs
172       # These need to be at the start of the arglist for full effectiveness.
173       # -q means quiet most stderr warnings.
174       # -l /dev/null means log to /dev/null.
175       sel_ldr_quiet_options = ['-q', '-l', '/dev/null']
176     if env.debug:
177       # Disabling validation (-c) is used by the debug stub test.
178       # TODO(dschuff): remove if/when it's no longer necessary
179       sel_ldr_options += ['-c', '-c', '-g']
180
181     # Tell the user
182     if is_dynamic:
183       extra = 'DYNAMIC'
184     else:
185       extra = 'STATIC'
186     PrintBanner('%s is %s %s' % (os.path.basename(nexe),
187                                  arch.upper(), extra))
188
189     # Setup architecture-specific environment variables
190     SetupArch(arch)
191
192     # Setup LibC-specific environment variables
193     SetupLibC(arch, is_dynamic)
194
195     # Add IRT to sel_ldr options.
196     if env.irt:
197       sel_ldr_options += ['-B', env.irt]
198
199     # The NaCl dynamic loader prefers posixy paths.
200     nexe_path = os.path.abspath(nexe)
201     nexe_path = nexe_path.replace('\\', '/')
202     sel_ldr_nexe_args = [nexe_path] + nexe_params
203
204     if is_dynamic:
205       ld_library_path = ':'.join(env.library_path)
206       if arch == 'arm':
207         sel_ldr_nexe_args = [env.elf_loader, '--interp-prefix',
208                              env.nnacl_tooldir] + sel_ldr_nexe_args
209         sel_ldr_options += ['-E', 'LD_LIBRARY_PATH=' + ld_library_path]
210       else:
211         sel_ldr_nexe_args = [env.runnable_ld, '--library-path',
212                              ld_library_path] + sel_ldr_nexe_args
213
214     # Setup sel_ldr arguments.
215     sel_ldr_args = sel_ldr_options + ['--'] + sel_ldr_nexe_args
216
217     # Run sel_ldr!
218     retries = 0
219     try:
220       if hasattr(env, 'retries'):
221         retries = int(env.retries)
222     except ValueError:
223       pass
224     collate = env.collate or retries > 0
225     input = sys.stdin.read() if collate else None
226     for iter in range(1 + max(retries, 0)):
227       output = RunSelLdr(sel_ldr_args, quiet_args=sel_ldr_quiet_options,
228                          collate=collate, stdin_string=input)
229       if env.last_return_code < 128:
230         # If the application crashes, we expect a 128+ return code.
231         break
232     sys.stdout.write(output or '')
233     return_code = env.last_return_code
234   finally:
235     if env.is_pnacl:
236       # Clean up the .nexe that was created.
237       try:
238         os.remove(nexe)
239       except:
240         pass
241
242   return return_code
243
244
245 def RunSelLdr(args, quiet_args=[], collate=False, stdin_string=None):
246   """Run the sel_ldr command and optionally capture its output.
247
248   Args:
249     args: A string list containing the command and arguments.
250     collate: Whether to capture stdout+stderr (rather than passing
251         them through to the terminal).
252     stdin_string: Text to send to the command via stdin.  If None, stdin is
253         inherited from the caller.
254
255   Returns:
256     A string containing the concatenation of any captured stdout plus
257     any captured stderr.
258   """
259   prefix = []
260   # The bootstrap loader args (--r_debug, --reserved_at_zero) need to
261   # come before quiet_args.
262   bootstrap_loader_args = []
263   arch = pynacl.platform.GetArch3264()
264   if arch != pynacl.platform.ARCH3264_ARM and env.arch == 'arm':
265     prefix = [ env.qemu_arm, '-cpu', 'cortex-a9']
266     if env.trace:
267       prefix += ['-d', 'in_asm,op,exec,cpu']
268     args = ['-Q'] + args
269
270   if arch != pynacl.platform.ARCH3264_MIPS32 and env.arch == 'mips32':
271     prefix = [env.qemu_mips32]
272     if env.trace:
273       prefix += ['-d', 'in_asm,op,exec,cpu']
274     args = ['-Q'] + args
275
276   # Use the bootstrap loader on linux.
277   if pynacl.platform.IsLinux():
278     bootstrap = os.path.join(os.path.dirname(env.sel_ldr),
279                              'nacl_helper_bootstrap')
280     loader = [bootstrap, env.sel_ldr]
281     template_digits = 'X' * 16
282     bootstrap_loader_args = ['--r_debug=0x' + template_digits,
283                              '--reserved_at_zero=0x' + template_digits]
284   else:
285     loader = [env.sel_ldr]
286   return Run(prefix + loader + bootstrap_loader_args + quiet_args + args,
287              exit_on_failure=(not collate),
288              capture_stdout=collate, capture_stderr=collate,
289              stdin_string=stdin_string)
290
291
292 def FindOrBuildIRT(allow_build = True):
293   if env.force_irt:
294     if env.force_irt == 'none':
295       return None
296     elif env.force_irt == 'core':
297       flavors = ['irt_core']
298     else:
299       irt = env.force_irt
300       if not os.path.exists(irt):
301         Fatal('IRT not found: %s' % irt)
302       return irt
303   else:
304     flavors = ['irt_core']
305
306   irt_paths = []
307   for flavor in flavors:
308     path = os.path.join(env.nacl_root, 'scons-out',
309                         'nacl_irt-%s/staging/%s.nexe' % (env.arch, flavor))
310     irt_paths.append(path)
311
312   for path in irt_paths:
313     if os.path.exists(path):
314       return path
315
316   if allow_build:
317     PrintBanner('irt not found. Building it with scons.')
318     irt = irt_paths[0]
319     BuildIRT(flavors[0])
320     assert(env.dry_run or os.path.exists(irt))
321     return irt
322
323   return None
324
325 def BuildIRT(flavor):
326   args = ('platform=%s naclsdk_validate=0 ' +
327           'sysinfo=0 -j8 %s') % (env.arch, flavor)
328   args = args.split()
329   Run([env.scons] + args, cwd=env.nacl_root)
330
331 def FindOrBuildElfLoader(allow_build=True):
332   if env.force_elf_loader:
333     if env.force_elf_loader == 'none':
334       return None
335     if not os.path.exists(env.force_elf_loader):
336       Fatal('elf_loader.nexe not found: %s' % env.force_elf_loader)
337     return env.force_elf_loader
338
339   path = os.path.join(env.nacl_root, 'scons-out',
340                       'nacl-' + env.arch,
341                       'staging', 'elf_loader.nexe')
342   if os.path.exists(path):
343     return path
344
345   if allow_build:
346     PrintBanner('elf_loader not found.  Building it with scons.')
347     Run([env.scons, 'platform=' + env.arch,
348          'naclsdk_validate=0', 'sysinfo=0',
349          '-j8', 'elf_loader'],
350         cwd=env.nacl_root)
351     assert(env.dry_run or os.path.exists(path))
352     return path
353
354   return None
355
356 def FindOrBuildSelLdr(allow_build=True):
357   if env.force_sel_ldr:
358     if env.force_sel_ldr in ('dbg','opt'):
359       modes = [ env.force_sel_ldr ]
360     else:
361       sel_ldr = env.force_sel_ldr
362       if not os.path.exists(sel_ldr):
363         Fatal('sel_ldr not found: %s' % sel_ldr)
364       return sel_ldr
365   else:
366     modes = ['opt','dbg']
367
368   loaders = []
369   for mode in modes:
370     sel_ldr = os.path.join(
371         env.nacl_root, 'scons-out',
372         '%s-%s-%s' % (mode, pynacl.platform.GetOS(), env.arch),
373         'staging', 'sel_ldr')
374     if pynacl.platform.IsWindows():
375       sel_ldr += '.exe'
376     loaders.append(sel_ldr)
377
378   # If one exists, use it.
379   for sel_ldr in loaders:
380     if os.path.exists(sel_ldr):
381       return sel_ldr
382
383   # Build it
384   if allow_build:
385     PrintBanner('sel_ldr not found. Building it with scons.')
386     sel_ldr = loaders[0]
387     BuildSelLdr(modes[0])
388     assert(env.dry_run or os.path.exists(sel_ldr))
389     return sel_ldr
390
391   return None
392
393 def BuildSelLdr(mode):
394   args = ('platform=%s MODE=%s-host naclsdk_validate=0 ' +
395           'sysinfo=0 -j8 sel_ldr') % (env.arch, mode)
396   args = args.split()
397   Run([env.scons] + args, cwd=env.nacl_root)
398
399 def Translate(arch, pexe):
400   output_file = os.path.splitext(pexe)[0] + '.' + arch + '.nexe'
401   pnacl_translate = os.path.join(env.pnacl_base, 'bin', 'pnacl-translate')
402   args = [ pnacl_translate, '-arch', arch, pexe, '-o', output_file,
403            '--allow-llvm-bitcode-input' ]
404   Run(args)
405   return output_file
406
407 def Stringify(args):
408   ret = ''
409   for arg in args:
410     if ' ' in arg:
411       ret += ' "%s"' % arg
412     else:
413       ret += ' %s' % arg
414   return ret.strip()
415
416 def PrepareStdin(stdin_string):
417   """Prepare a stdin stream for a subprocess based on contents of a string.
418   This has to be in the form of an actual file, rather than directly piping
419   the string, since the child may (inappropriately) try to fseek() on stdin.
420
421   Args:
422     stdin_string: The characters to pipe to the subprocess.
423
424   Returns:
425     An open temporary file object ready to be read from.
426   """
427   f = tempfile.TemporaryFile()
428   f.write(stdin_string)
429   f.seek(0)
430   return f
431
432 def Run(args, cwd=None, verbose=True, exit_on_failure=False,
433         capture_stdout=False, capture_stderr=False, stdin_string=None):
434   """Run a command and optionally capture its output.
435
436   Args:
437     args: A string list containing the command and arguments.
438     cwd: Change to this directory before running.
439     verbose: Print the command before running it.
440     exit_on_failure: Exit immediately if the command returns nonzero.
441     capture_stdout: Capture the stdout as a string (rather than passing it
442         through to the terminal).
443     capture_stderr: Capture the stderr as a string (rather than passing it
444         through to the terminal).
445     stdin_string: Text to send to the command via stdin.  If None, stdin is
446         inherited from the caller.
447
448   Returns:
449     A string containing the concatenation of any captured stdout plus
450     any captured stderr.
451   """
452   if verbose:
453     PrintCommand(Stringify(args))
454     if env.dry_run:
455       return
456
457   stdout_redir = None
458   stderr_redir = None
459   stdin_redir = None
460   if capture_stdout:
461     stdout_redir = subprocess.PIPE
462   if capture_stderr:
463     stderr_redir = subprocess.PIPE
464   if stdin_string:
465     stdin_redir = PrepareStdin(stdin_string)
466
467   p = None
468   try:
469     # PNaCl toolchain executables (pnacl-translate, readelf) are scripts
470     # not binaries, so it doesn't want to run on Windows without a shell.
471     use_shell = True if pynacl.platform.IsWindows() else False
472     p = subprocess.Popen(args, stdin=stdin_redir, stdout=stdout_redir,
473                          stderr=stderr_redir, cwd=cwd, shell=use_shell)
474     (stdout_contents, stderr_contents) = p.communicate()
475   except KeyboardInterrupt, e:
476     if p:
477       p.kill()
478     raise e
479   except BaseException, e:
480     if p:
481       p.kill()
482     raise e
483
484   env.last_return_code = p.returncode
485
486   if p.returncode != 0 and exit_on_failure:
487     if capture_stdout or capture_stderr:
488       # Print an extra message if any of the program's output wasn't
489       # going to the screen.
490       Fatal('Failed to run: %s' % Stringify(args))
491     sys.exit(p.returncode)
492
493   return (stdout_contents or '') + (stderr_contents or '')
494
495
496 def ArgSplit(argv):
497   """Parse command-line arguments.
498
499   Returns:
500     Tuple (nexe, nexe_args) where nexe is the name of the nexe or pexe
501     to execute, and nexe_args are its runtime arguments.
502   """
503   desc = ('Run a command-line nexe (or pexe). Automatically handles\n' +
504           'translation, building sel_ldr, and building the IRT.')
505   parser = argparse.ArgumentParser(description=desc)
506   parser.add_argument('-L', action='append', dest='library_path', default=[],
507                       help='Additional library path for dynamic linker.')
508   parser.add_argument('--paranoid', action='store_true', default=False,
509                       help='Remove -S (signals) and -a (file access) ' +
510                       'from the default sel_ldr options, and disallow mmap() ' +
511                       'for loading the nexe.')
512   parser.add_argument('--loader', dest='force_sel_ldr', metavar='SEL_LDR',
513                       help='Path to sel_ldr.  "dbg" or "opt" means use ' +
514                       'dbg or opt version of sel_ldr. ' +
515                       'By default, use whichever sel_ldr already exists; ' +
516                       'otherwise, build opt version.')
517   parser.add_argument('--irt', dest='force_irt', metavar='IRT',
518                       help='Path to IRT nexe.  "core" or "none" means use ' +
519                       'Core IRT or no IRT.  By default, use whichever IRT ' +
520                       'already exists; otherwise, build irt_core.')
521   parser.add_argument('--elf_loader', dest='force_elf_loader',
522                       metavar='ELF_LOADER',
523                       help='Path to elf_loader nexe.  ' +
524                       'By default find it in scons-out, or build it.')
525   parser.add_argument('--dry-run', '-n', action='store_true', default=False,
526                       help="Just print commands, don't execute them.")
527   parser.add_argument('--quiet', '-q', action='store_true', default=False,
528                       help="Don't print anything.")
529   parser.add_argument('--retries', default='0', metavar='N',
530                       help='Retry sel_ldr command up to N times (if ' +
531                       'flakiness is expected).  This argument implies ' +
532                       '--collate.')
533   parser.add_argument('--collate', action='store_true', default=False,
534                       help="Combine/collate sel_ldr's stdout and stderr, and " +
535                       "print to stdout.")
536   parser.add_argument('--trace', '-t', action='store_true', default=False,
537                       help='Trace qemu execution.')
538   parser.add_argument('--debug', '-g', action='store_true', default=False,
539                       help='Run sel_ldr with debugging enabled.')
540   parser.add_argument('-arch', '-m', dest='arch', action='store',
541                       choices=sorted(
542                         pynacl.platform.ARCH3264_LIST + ['env']),
543                       help=('Specify architecture for PNaCl translation. ' +
544                             '"env" is a special value which obtains the ' +
545                             'architecture from the environment ' +
546                             'variable "%s".') % ARCH_ENV_VAR_NAME)
547   parser.add_argument('remainder', nargs=argparse.REMAINDER,
548                       metavar='nexe/pexe + args')
549   (options, args) = parser.parse_known_args(argv)
550
551   # Copy the options into env.
552   for (key, value) in vars(options).iteritems():
553     setattr(env, key, value)
554
555   args += options.remainder
556   nexe = args[0] if len(args) else ''
557   env.is_pnacl = nexe.endswith('.pexe')
558
559   if env.arch == 'env':
560     # Get the architecture from the environment.
561     try:
562       env.arch = os.environ[ARCH_ENV_VAR_NAME]
563     except Exception as e:
564       Fatal(('Option "-arch env" specified, but environment variable ' +
565             '"%s" not specified: %s') % (ARCH_ENV_VAR_NAME, e))
566   if not env.arch and env.is_pnacl:
567     # For NaCl we'll figure out the architecture from the nexe's
568     # architecture, but for PNaCl we first need to translate and the
569     # user didn't tell us which architecture to translate to. Be nice
570     # and just translate to the current machine's architecture.
571     env.arch = pynacl.platform.GetArch3264()
572   # Canonicalize env.arch.
573   env.arch = pynacl.platform.GetArch3264(env.arch)
574   return nexe, args[1:]
575
576
577 def Fatal(msg, *args):
578   if len(args) > 0:
579     msg = msg % args
580   print(msg)
581   sys.exit(1)
582
583
584 def FindReadElf():
585   '''Returns the path of "readelf" binary.'''
586
587   candidates = []
588   # Use PNaCl's if it available.
589   candidates.append(
590     os.path.join(env.pnacl_base, 'bin', 'pnacl-readelf'))
591
592   # Otherwise, look for the system readelf
593   for path in os.environ['PATH'].split(os.pathsep):
594     candidates.append(os.path.join(path, 'readelf'))
595
596   for readelf in candidates:
597     if os.path.exists(readelf):
598       return readelf
599
600   Fatal('Cannot find readelf!')
601
602
603 def ReadELFInfo(f):
604   ''' Returns: (arch, is_dynamic, is_glibc_static) '''
605
606   readelf = env.readelf
607   readelf_out = Run([readelf, '-lh', f], capture_stdout=True, verbose=False)
608
609   machine_line = None
610   is_dynamic = False
611   is_glibc_static = False
612   for line in readelf_out.split('\n'):
613     line = line.strip()
614     if line.startswith('Machine:'):
615       machine_line = line
616     if line.startswith('DYNAMIC'):
617       is_dynamic = True
618     if '__libc_atexit' in line:
619       is_glibc_static = True
620
621   if not machine_line:
622     Fatal('Script error: readelf output did not make sense!')
623
624   if 'Intel 80386' in machine_line:
625     arch = 'x86-32'
626   elif 'X86-64' in machine_line:
627     arch = 'x86-64'
628   elif 'ARM' in machine_line:
629     arch = 'arm'
630   elif 'MIPS' in machine_line:
631     arch = 'mips32'
632   else:
633     Fatal('%s: Unknown machine type', f)
634
635   return (arch, is_dynamic, is_glibc_static)
636
637
638 def FindBaseDir():
639   '''Crawl backwards, starting from the directory containing this script,
640      until we find the native_client/ directory.
641   '''
642
643   curdir = os.path.abspath(sys.argv[0])
644   while os.path.basename(curdir) != 'native_client':
645     curdir,subdir = os.path.split(curdir)
646     if subdir == '':
647       # We've hit the file system root
648       break
649
650   if os.path.basename(curdir) != 'native_client':
651     Fatal('Unable to find native_client directory!')
652   return curdir
653
654
655 if __name__ == '__main__':
656   sys.exit(main(sys.argv))