| OLD | NEW |
| (Empty) |
| 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 # IMPORTANT NOTE: If you make local mods to this file, you must run: | |
| 7 # % pnacl/build.sh driver | |
| 8 # in order for them to take effect in the scons build. This command | |
| 9 # updates the copy in the toolchain/ tree. | |
| 10 # | |
| 11 | |
| 12 # Generate a draft manifest file given bitcode pexe. | |
| 13 # URLs and unknown runtime dependencies may be manually changed afterwards. | |
| 14 | |
| 15 from driver_tools import * | |
| 16 from driver_env import env | |
| 17 from driver_log import Log, DriverOpen, TempFiles | |
| 18 from collections import deque | |
| 19 import filetype | |
| 20 | |
| 21 import hashlib | |
| 22 import json | |
| 23 | |
| 24 EXTRA_ENV = { | |
| 25 'INPUTS' : '', | |
| 26 'OUTPUT' : '', | |
| 27 | |
| 28 'BASELINE_NMF' : '', | |
| 29 | |
| 30 # FLAT_URL_SCHEME is '1' if we are just trying to test via scons and | |
| 31 # do not want to put libraries under any kind of directory structure | |
| 32 # to simplify file staging. This means that really only one of the files | |
| 33 # is valid, since all arches will have the same URL. This is only for tests. | |
| 34 'FLAT_URL_SCHEME' : '0', | |
| 35 | |
| 36 # Search path for finding pso's to grab transitive dependencies. | |
| 37 'SEARCH_DIRS' : '${SEARCH_DIRS_USER} ${SEARCH_DIRS_BUILTIN}', | |
| 38 'SEARCH_DIRS_USER' : '', | |
| 39 # This doesn't include native lib directories, so we aren't | |
| 40 # tracing transitive dependencies for those. | |
| 41 'SEARCH_DIRS_BUILTIN': ' ${BASE_USR}/lib/ ' + | |
| 42 ' ${BASE_SDK}/lib/ ' + | |
| 43 ' ${BASE_LIB}/ ', | |
| 44 } | |
| 45 | |
| 46 NMF_PATTERNS = [ | |
| 47 ( ('-L', '(.+)'), | |
| 48 "env.append('SEARCH_DIRS_USER', pathtools.normalize($0))\n"), | |
| 49 ( '-L(.+)', | |
| 50 "env.append('SEARCH_DIRS_USER', pathtools.normalize($0))\n"), | |
| 51 ( '--library-path=(.+)', | |
| 52 "env.append('SEARCH_DIRS_USER', pathtools.normalize($0))\n"), | |
| 53 ( ('--base-nmf', '(.+)'), | |
| 54 "env.set('BASELINE_NMF', pathtools.normalize($0))\n"), | |
| 55 | |
| 56 ( ('-o', '(.+)'), "env.set('OUTPUT', pathtools.normalize($0))\n"), | |
| 57 ( '--flat-url-scheme', "env.set('FLAT_URL_SCHEME', '1')"), | |
| 58 | |
| 59 ( '(.*)', "env.append('INPUTS', pathtools.normalize($0))"), | |
| 60 ] | |
| 61 | |
| 62 def get_help(argv): | |
| 63 return """ | |
| 64 NaCl Manifest Generator for PNaCl. | |
| 65 | |
| 66 Usage: %s [options] <pexe_file> | |
| 67 | |
| 68 BASIC OPTIONS: | |
| 69 -o <file> Output to <file> | |
| 70 --base-nmf <file> Use a baseline NMF file as a starting point. | |
| 71 -L <dir> Add library search path | |
| 72 --library-path=<dir> Ditto. | |
| 73 --flat-url-scheme For testing: Do not nest architecture-specific | |
| 74 files in an architecture-specific directory | |
| 75 when generating URLs. | |
| 76 """ % env.getone('SCRIPT_NAME') | |
| 77 | |
| 78 def AddUrlForAllArches(json_dict, shortname): | |
| 79 KNOWN_NMF_ARCHES = ['x86-32', 'x86-64', 'arm'] | |
| 80 for arch in KNOWN_NMF_ARCHES: | |
| 81 json_dict[arch] = {} | |
| 82 if env.getbool('FLAT_URL_SCHEME'): | |
| 83 json_dict[arch]['url'] = '%s' % shortname | |
| 84 else: | |
| 85 json_dict[arch]['url'] = 'lib-%s/%s' % (arch, shortname) | |
| 86 | |
| 87 def SetPortableUrl(json_dict, shortname, filepath): | |
| 88 json_dict['portable'] = {} | |
| 89 json_dict['portable']['pnacl-translate'] = {} | |
| 90 json_dict['portable']['pnacl-translate']['url'] = shortname | |
| 91 fd = DriverOpen(filepath, 'r') | |
| 92 sha = hashlib.sha256() | |
| 93 sha.update(fd.read()) | |
| 94 fd.close() | |
| 95 json_dict['portable']['pnacl-translate']['sha256'] = sha.hexdigest() | |
| 96 | |
| 97 def GetActualFilePathAndType(shortname): | |
| 98 actual_lib_path = ldtools.FindFile([shortname], | |
| 99 env.get('SEARCH_DIRS'), | |
| 100 ldtools.LibraryTypes.ANY) | |
| 101 if actual_lib_path == None: | |
| 102 Log.Warning('Could not find path of lib: %s, assuming it is native', | |
| 103 shortname) | |
| 104 file_type = 'so' | |
| 105 else: | |
| 106 file_type = filetype.FileType(actual_lib_path) | |
| 107 return actual_lib_path, file_type | |
| 108 | |
| 109 def GetTransitiveClosureOfNeeded(base_needed): | |
| 110 visited_portable = set() | |
| 111 visited_native = set() | |
| 112 all_needed = set(base_needed) | |
| 113 worklist = deque(base_needed) | |
| 114 while len(worklist) != 0: | |
| 115 lib = worklist.popleft() | |
| 116 if lib in visited_portable or lib in visited_native: | |
| 117 continue | |
| 118 actual_lib_path, file_type = GetActualFilePathAndType(lib) | |
| 119 if file_type == 'so': | |
| 120 # We could trace if we used objdump / readelf... | |
| 121 Log.Warning('Not tracing dependencies of native lib %s', lib) | |
| 122 visited_native.add(lib) | |
| 123 continue | |
| 124 elif file_type == 'pso': | |
| 125 visited_portable.add(lib) | |
| 126 more_needed = filetype.GetBitcodeMetadata( | |
| 127 actual_lib_path).get('NeedsLibrary', []) | |
| 128 all_needed |= set(more_needed) | |
| 129 worklist.extend(more_needed) | |
| 130 else: | |
| 131 Log.Fatal('Lib: %s is neither bitcode nor native', lib) | |
| 132 return all_needed | |
| 133 | |
| 134 def ReadBaselineNMF(baseline_file): | |
| 135 f = DriverOpen(baseline_file, 'r') | |
| 136 json_dict = json.load(f) | |
| 137 f.close() | |
| 138 return json_dict | |
| 139 | |
| 140 def EchoBaselineNMF(baseline_nmf, output_file): | |
| 141 json.dump(baseline_nmf, output_file, sort_keys=True, indent=2) | |
| 142 | |
| 143 def GenerateStaticNMF(pexe_file, baseline_nmf, output_file): | |
| 144 nmf_json = baseline_nmf | |
| 145 nmf_json['program'] = {} | |
| 146 SetPortableUrl(nmf_json['program'], | |
| 147 pathtools.basename(pexe_file), | |
| 148 pexe_file) | |
| 149 json.dump(nmf_json, output_file, sort_keys=True, indent=2) | |
| 150 | |
| 151 | |
| 152 def GenerateDynamicNMF(pexe_file, baseline_nmf, needed, output_file): | |
| 153 nmf_json = baseline_nmf | |
| 154 # Set runnable-ld.so as the program interpreter. | |
| 155 nmf_json['program'] = {} | |
| 156 AddUrlForAllArches(nmf_json['program'], 'runnable-ld.so') | |
| 157 # Set the pexe as the main program. | |
| 158 nmf_json['files'] = baseline_nmf.get('files', {}) | |
| 159 nmf_json['files']['main.nexe'] = {} | |
| 160 SetPortableUrl(nmf_json['files']['main.nexe'], | |
| 161 pathtools.basename(pexe_file), | |
| 162 pexe_file) | |
| 163 | |
| 164 # Get transitive closure of needed libraries. | |
| 165 transitive_needed = GetTransitiveClosureOfNeeded(needed) | |
| 166 | |
| 167 # Make urls for libraries. | |
| 168 for lib in transitive_needed: | |
| 169 nmf_json['files'][lib] = {} | |
| 170 actual_lib_path, file_type = GetActualFilePathAndType(lib) | |
| 171 if file_type == 'so': | |
| 172 # Assume a native version exists for every known arch. | |
| 173 AddUrlForAllArches(nmf_json['files'][lib], lib) | |
| 174 elif file_type == 'pso': | |
| 175 SetPortableUrl(nmf_json['files'][lib], | |
| 176 lib, | |
| 177 actual_lib_path) | |
| 178 else: | |
| 179 Log.Fatal('Needed library is not a .so nor a .pso') | |
| 180 json.dump(nmf_json, output_file, sort_keys=True, indent=2) | |
| 181 | |
| 182 | |
| 183 def main(argv): | |
| 184 env.update(EXTRA_ENV) | |
| 185 ParseArgs(argv, NMF_PATTERNS) | |
| 186 exe_file = env.getone('INPUTS') | |
| 187 | |
| 188 if not exe_file: | |
| 189 Log.Fatal('Expecting input exe_file') | |
| 190 DriverExit(1) | |
| 191 | |
| 192 baseline_file = env.getone('BASELINE_NMF') | |
| 193 if not baseline_file: | |
| 194 baseline_nmf = {} | |
| 195 else: | |
| 196 baseline_nmf = ReadBaselineNMF(baseline_file) | |
| 197 | |
| 198 output = env.getone('OUTPUT') | |
| 199 if output == '': | |
| 200 output_file = sys.stdout | |
| 201 else: | |
| 202 output_file = DriverOpen(output, 'w') | |
| 203 | |
| 204 if not FileType(exe_file) == 'pexe': | |
| 205 EchoBaselineNMF(baseline_nmf, output_file) | |
| 206 return 0 | |
| 207 needed = GetBitcodeMetadata(exe_file).get('NeedsLibrary', []) | |
| 208 if len(needed) == 0: | |
| 209 GenerateStaticNMF(exe_file, baseline_nmf, output_file) | |
| 210 else: | |
| 211 # runnable_ld.so is going to ask for libgcc_s.so.1 even though it does | |
| 212 # not show up in the pexe's NEEDED metadata, so it won't show up | |
| 213 # in the NMF and on the App's webserver. | |
| 214 # | |
| 215 # For now, hack in libgcc_s.so.1. | |
| 216 # http://code.google.com/p/nativeclient/issues/detail?id=2582 | |
| 217 needed.append('libgcc_s.so.1') | |
| 218 GenerateDynamicNMF(exe_file, baseline_nmf, needed, output_file) | |
| 219 DriverClose(output_file) | |
| 220 return 0 | |
| OLD | NEW |