Index: pnacl/driver/pnacl-nmf.py |
diff --git a/pnacl/driver/pnacl-nmf.py b/pnacl/driver/pnacl-nmf.py |
new file mode 100755 |
index 0000000000000000000000000000000000000000..2b8169606dbf83eae5b976f7c2e6eff6abbf3c84 |
--- /dev/null |
+++ b/pnacl/driver/pnacl-nmf.py |
@@ -0,0 +1,175 @@ |
+#!/usr/bin/python |
+# Copyright (c) 2012 The Native Client Authors. All rights reserved. |
+# Use of this source code is governed by a BSD-style license that can be |
+# found in the LICENSE file. |
+# |
+# IMPORTANT NOTE: If you make local mods to this file, you must run: |
+# % pnacl/build.sh driver |
+# in order for them to take effect in the scons build. This command |
+# updates the copy in the toolchain/ tree. |
+# |
+ |
+# Generate a draft manifest file given bitcode pexe. |
+# URLs and unknown runtime dependencies may be manually changed afterwards. |
+ |
+from driver_tools import * |
+from driver_env import env |
+from driver_log import Log, DriverOpen, TempFiles |
+from collections import deque |
+ |
+try: |
+ import json |
+except Exception: |
+ import simplejson as json |
+ |
+EXTRA_ENV = { |
+ 'INPUTS' : '', |
+ 'OUTPUT' : '', |
+ |
+ # FLAT_URL_SCHEME is '1' if we are just trying to test via scons and |
+ # do not want to put libraries under any kind of directory structure |
+ # to simplify file staging. This means that really only one of the files |
+ # is valid, since all arches will have the same URL. This is only for tests. |
+ 'FLAT_URL_SCHEME' : '0', |
+ |
+ # Search path for finding pso's to grab transitive dependencies. |
+ 'SEARCH_DIRS' : '${SEARCH_DIRS_USER} ${SEARCH_DIRS_BUILTIN}', |
+ 'SEARCH_DIRS_USER' : '', |
+ # This doesn't include native lib directories, so we aren't |
+ # tracing transitive dependencies for those. |
+ 'SEARCH_DIRS_BUILTIN': ' ${BASE_USR}/lib/ ' + |
+ ' ${BASE_SDK}/lib/ ' + |
+ ' ${BASE_LIB}/ ', |
+} |
+env.update(EXTRA_ENV) |
+ |
+NMF_PATTERNS = [ |
+ ( ('-L', '(.+)'), |
+ "env.append('SEARCH_DIRS_USER', pathtools.normalize($0))\n"), |
+ ( '-L(.+)', |
+ "env.append('SEARCH_DIRS_USER', pathtools.normalize($0))\n"), |
+ ( '--library-path=(.+)', |
+ "env.append('SEARCH_DIRS_USER', pathtools.normalize($0))\n"), |
+ |
+ ( ('-o', '(.+)'), "env.set('OUTPUT', pathtools.normalize($0))\n"), |
+ ( '--flat-url-scheme', "env.set('FLAT_URL_SCHEME', '1')"), |
+ |
+ ( '(.*)', "env.append('INPUTS', pathtools.normalize($0))"), |
+] |
+ |
+def Usage(): |
+ print "Usage: pnacl-nmf <pexe_file> [-L=...]" |
+ print "Generate a draft NMF file based on the given pexe file." |
+ |
+def AddUrlForAllArches(json_dict, shortname): |
+ # TODO(jvoung): We may want to calculate a hash to go with the URL. |
+ KNOWN_NMF_ARCHES = ['x86-32', 'x86-64', 'arm'] |
+ for arch in KNOWN_NMF_ARCHES: |
+ json_dict[arch] = {} |
+ if env.getbool('FLAT_URL_SCHEME'): |
+ json_dict[arch]['url'] = '%s' % shortname |
+ else: |
+ json_dict[arch]['url'] = 'lib-%s/%s' % (arch, shortname) |
+ |
+def SetPortableUrl(json_dict, shortname): |
+ # TODO(jvoung): We may want to calculate a hash to go with the URL. |
+ json_dict['portable'] = {} |
+ json_dict['portable']['url'] = shortname |
+ |
+def GetActualFilePathAndType(shortname): |
+ actual_lib_path = ldtools.FindFile([shortname], env.get('SEARCH_DIRS')) |
+ if actual_lib_path == None: |
+ Log.Warning('Could not find path of lib: %s, assuming it is native', |
+ shortname) |
+ file_type = 'so' |
+ else: |
+ file_type = FileType(actual_lib_path) |
+ return actual_lib_path, file_type |
+ |
+def GetTransitiveClosureOfNeeded(base_needed): |
+ # TODO(jvoung): We may need to get the topological sort of depedencies. |
+ visited_portable = set() |
+ visited_native = set() |
+ all_needed = set(base_needed) |
+ worklist = deque(base_needed) |
+ while len(worklist) != 0: |
+ lib = worklist.popleft() |
+ if lib in visited_portable or lib in visited_native: |
+ continue |
+ actual_lib_path, file_type = GetActualFilePathAndType(lib) |
+ if file_type == 'so': |
+ # We could trace if we used objdump / readelf... |
+ Log.Warning('Not tracing dependencies of native lib %s', lib) |
+ visited_native.add(lib) |
+ continue |
+ elif file_type == 'pso': |
+ visited_portable.add(lib) |
+ more_needed = GetBitcodeMetadata(actual_lib_path).get('NeedsLibrary', []) |
+ all_needed |= set(more_needed) |
+ worklist.extend(more_needed) |
+ else: |
+ Log.Fatal('Lib: %s is neither bitcode nor native', lib) |
+ return all_needed |
+ |
+ |
+def GenerateStaticNMF(pexe_file, output_file): |
+ nmf_json = {} |
+ nmf_json['program'] = {} |
+ SetPortableUrl(nmf_json['program'], pathtools.basename(pexe_file)) |
+ json.dump(nmf_json, output_file, sort_keys=True, indent=2) |
+ |
+ |
+def GenerateDynamicNMF(pexe_file, needed, output_file): |
+ nmf_json = {} |
+ # Set runnable-ld.so as the program interpreter. |
+ nmf_json['program'] = {} |
+ AddUrlForAllArches(nmf_json['program'], 'runnable-ld.so') |
+ # Set the pexe as the main program. |
+ nmf_json['files'] = {} |
+ nmf_json['files']['main.nexe'] = {} |
+ SetPortableUrl(nmf_json['files']['main.nexe'], pathtools.basename(pexe_file)) |
+ |
+ # Get transitive closure of needed libraries. |
+ transitive_needed = GetTransitiveClosureOfNeeded(needed) |
+ |
+ # Make urls for libraries. |
+ for lib in transitive_needed: |
+ nmf_json['files'][lib] = {} |
+ actual_lib_path, file_type = GetActualFilePathAndType(lib) |
+ if file_type == 'so': |
+ # Assume a native version exists for every known arch. |
+ AddUrlForAllArches(nmf_json['files'][lib], lib) |
+ elif file_type == 'pso': |
+ SetPortableUrl(nmf_json['files'][lib], lib) |
+ else: |
+ Log.Fatal('Needed library is not a .so nor a .pso') |
+ json.dump(nmf_json, output_file, sort_keys=True, indent=2) |
+ |
+ |
+def main(argv): |
+ ParseArgs(argv, NMF_PATTERNS) |
+ inputs = env.get('INPUTS') |
+ |
+ if not inputs or len(inputs) != 1: |
+ Usage() |
+ DriverExit(0) |
+ pexe_file = inputs[0] |
+ output = env.getone('OUTPUT') |
+ if output == '': |
+ output_file = sys.stdout |
+ else: |
+ output_file = DriverOpen(output, 'w') |
+ |
+ if not FileType(pexe_file) == 'pexe': |
+ Log.Fatal("File %s is not an executable bitcode file", |
+ pathtools.touser(pexe_file)) |
+ needed = GetBitcodeMetadata(pexe_file).get('NeedsLibrary', []) |
+ if len(needed) == 0: |
+ GenerateStaticNMF(pexe_file, output_file) |
+ else: |
+ GenerateDynamicNMF(pexe_file, needed, output_file) |
+ DriverClose(output_file) |
+ return 0 |
+ |
+if __name__ == "__main__": |
+ DriverMain(main) |