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 |
(...skipping 11 matching lines...) Expand all Loading... |
22 NeededMatcher = re.compile('^ *NEEDED *([^ ]+)\n$') | 22 NeededMatcher = re.compile('^ *NEEDED *([^ ]+)\n$') |
23 FormatMatcher = re.compile('^(.+):\\s*file format (.+)\n$') | 23 FormatMatcher = re.compile('^(.+):\\s*file format (.+)\n$') |
24 | 24 |
25 FORMAT_ARCH_MAP = { | 25 FORMAT_ARCH_MAP = { |
26 # Names returned by Linux's objdump: | 26 # Names returned by Linux's objdump: |
27 'elf64-x86-64': 'x86-64', | 27 'elf64-x86-64': 'x86-64', |
28 'elf32-i386': 'x86-32', | 28 'elf32-i386': 'x86-32', |
29 # Names returned by x86_64-nacl-objdump: | 29 # Names returned by x86_64-nacl-objdump: |
30 'elf64-nacl': 'x86-64', | 30 'elf64-nacl': 'x86-64', |
31 'elf32-nacl': 'x86-32', | 31 'elf32-nacl': 'x86-32', |
32 # TODO(mball): Add support for 'arm-32' and 'portable' architectures | |
33 # 'elf32-little': 'arm-32', | |
34 } | 32 } |
35 | 33 |
| 34 ARCH_LOCATION = { |
| 35 'x86-32': 'lib32', |
| 36 'x86-64': 'lib64', |
| 37 } |
| 38 |
36 # These constants are used within nmf files. | 39 # These constants are used within nmf files. |
37 RUNNABLE_LD = 'runnable-ld.so' # Name of the dynamic loader | 40 RUNNABLE_LD = 'runnable-ld.so' # Name of the dynamic loader |
38 MAIN_NEXE = 'main.nexe' # Name of entry point for execution | 41 MAIN_NEXE = 'main.nexe' # Name of entry point for execution |
39 PROGRAM_KEY = 'program' # Key of the program section in an nmf file | 42 PROGRAM_KEY = 'program' # Key of the program section in an nmf file |
40 URL_KEY = 'url' # Key of the url field for a particular file in an nmf file | 43 URL_KEY = 'url' # Key of the url field for a particular file in an nmf file |
41 FILES_KEY = 'files' # Key of the files section in an nmf file | 44 FILES_KEY = 'files' # Key of the files section in an nmf file |
42 | 45 |
43 # The proper name of the dynamic linker, as kept in the IRT. This is | 46 # The proper name of the dynamic linker, as kept in the IRT. This is |
44 # excluded from the nmf file by convention. | 47 # excluded from the nmf file by convention. |
45 LD_NACL_MAP = { | 48 LD_NACL_MAP = { |
(...skipping 57 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
103 both for staging the libraries and for inclusion into the nmf file. | 106 both for staging the libraries and for inclusion into the nmf file. |
104 Examples: ['..'], ['lib_dir'] ''' | 107 Examples: ['..'], ['lib_dir'] ''' |
105 self.objdump = objdump | 108 self.objdump = objdump |
106 self.main_files = main_files or [] | 109 self.main_files = main_files or [] |
107 self.extra_files = extra_files or [] | 110 self.extra_files = extra_files or [] |
108 self.lib_path = lib_path or [] | 111 self.lib_path = lib_path or [] |
109 self.manifest = None | 112 self.manifest = None |
110 self.needed = None | 113 self.needed = None |
111 self.lib_prefix = lib_prefix or [] | 114 self.lib_prefix = lib_prefix or [] |
112 | 115 |
| 116 |
113 def GleanFromObjdump(self, files): | 117 def GleanFromObjdump(self, files): |
114 '''Get architecture and dependency information for given files | 118 '''Get architecture and dependency information for given files |
115 | 119 |
116 Args: | 120 Args: |
117 files: A dict with key=filename and value=list or set of archs. E.g.: | 121 files: A dict with key=filename and value=list or set of archs. E.g.: |
118 { '/path/to/my.nexe': ['x86-32', 'x86-64'], | 122 { '/path/to/my.nexe': ['x86-32'] |
119 '/path/to/libmy.so': ['x86-32'], | 123 '/path/to/lib64/libmy.so': ['x86-64'], |
120 '/path/to/my2.nexe': None } # Indicates all architectures | 124 '/path/to/mydata.so': ['x86-32', 'x86-64'], |
| 125 '/path/to/my.data': None } # Indicates all architectures |
121 | 126 |
122 Returns: A tuple with the following members: | 127 Returns: A tuple with the following members: |
123 input_info: A dict with key=filename and value=ArchFile of input files. | 128 input_info: A dict with key=filename and value=ArchFile of input files. |
124 Includes the input files as well, with arch filled in if absent. | 129 Includes the input files as well, with arch filled in if absent. |
125 Example: { '/path/to/my.nexe': ArchFile(my.nexe), | 130 Example: { '/path/to/my.nexe': ArchFile(my.nexe), |
126 '/path/to/libfoo.so': ArchFile(libfoo.so) } | 131 '/path/to/libfoo.so': ArchFile(libfoo.so) } |
127 needed: A set of strings formatted as "arch/name". Example: | 132 needed: A set of strings formatted as "arch/name". Example: |
128 set(['x86-32/libc.so', 'x86-64/libgcc.so']) | 133 set(['x86-32/libc.so', 'x86-64/libgcc.so']) |
129 ''' | 134 ''' |
130 DebugPrint("GleanFromObjdump(%s)" % ([self.objdump, '-p'] + files.keys())) | 135 DebugPrint("GleanFromObjdump(%s)" % ([self.objdump, '-p'] + files.keys())) |
131 proc = subprocess.Popen([self.objdump, '-p'] + files.keys(), | 136 proc = subprocess.Popen([self.objdump, '-p'] + files.keys(), |
132 stdout=subprocess.PIPE, | 137 stdout=subprocess.PIPE, |
133 stderr=subprocess.PIPE, bufsize=-1) | 138 stderr=subprocess.PIPE, bufsize=-1) |
134 input_info = {} | 139 input_info = {} |
135 needed = set() | 140 needed = set() |
136 output, err_output = proc.communicate() | 141 output, err_output = proc.communicate() |
137 for line in output.splitlines(True): | 142 for line in output.splitlines(True): |
138 # Objdump should display the architecture first and then the dependencies | 143 # Objdump should display the architecture first and then the dependencies |
139 # second for each file in the list. | 144 # second for each file in the list. |
140 matched = FormatMatcher.match(line) | 145 matched = FormatMatcher.match(line) |
141 if matched is not None: | 146 if matched is not None: |
142 filename = matched.group(1) | 147 filename = matched.group(1) |
143 arch = FORMAT_ARCH_MAP[matched.group(2)] | 148 arch = FORMAT_ARCH_MAP[matched.group(2)] |
144 if files[filename] is None or arch in files[filename]: | 149 if files[filename] is None or arch in files[filename]: |
145 name = os.path.basename(filename) | 150 name = os.path.basename(filename) |
146 input_info[filename] = ArchFile( | 151 input_info[filename] = ArchFile( |
147 arch=arch, | 152 arch=arch, |
148 name=name, | 153 name=name, |
149 path=filename, | 154 path=filename, |
150 url='/'.join(self.lib_prefix + [arch, name])) | 155 url='/'.join(self.lib_prefix + [ARCH_LOCATION[arch], name])) |
151 matched = NeededMatcher.match(line) | 156 matched = NeededMatcher.match(line) |
152 if matched is not None: | 157 if matched is not None: |
153 if files[filename] is None or arch in files[filename]: | 158 if files[filename] is None or arch in files[filename]: |
154 needed.add('/'.join([arch, matched.group(1)])) | 159 needed.add('/'.join([arch, matched.group(1)])) |
155 status = proc.poll() | 160 status = proc.poll() |
156 if status != 0: | 161 if status != 0: |
157 raise Error('%s\nStdError=%s\nobjdump failed with error code: %d' % | 162 raise Error('%s\nStdError=%s\nobjdump failed with error code: %d' % |
158 (output, err_output, status)) | 163 (output, err_output, status)) |
159 return input_info, needed | 164 return input_info, needed |
160 | 165 |
(...skipping 21 matching lines...) Expand all Loading... |
182 A dict with key=filename and value=ArchFile of input files. | 187 A dict with key=filename and value=ArchFile of input files. |
183 Includes the input files as well, with arch filled in if absent. | 188 Includes the input files as well, with arch filled in if absent. |
184 Example: { '/path/to/my.nexe': ArchFile(my.nexe), | 189 Example: { '/path/to/my.nexe': ArchFile(my.nexe), |
185 '/path/to/libfoo.so': ArchFile(libfoo.so) }''' | 190 '/path/to/libfoo.so': ArchFile(libfoo.so) }''' |
186 if not self.needed: | 191 if not self.needed: |
187 DebugPrint('GetNeeded(%s)' % self.main_files) | 192 DebugPrint('GetNeeded(%s)' % self.main_files) |
188 examined = set() | 193 examined = set() |
189 all_files, unexamined = self.GleanFromObjdump( | 194 all_files, unexamined = self.GleanFromObjdump( |
190 dict([(file, None) for file in self.main_files])) | 195 dict([(file, None) for file in self.main_files])) |
191 for name, arch_file in all_files.items(): | 196 for name, arch_file in all_files.items(): |
192 arch_file.url = os.path.basename(name) | 197 arch_file.url = name |
193 if unexamined: | 198 if unexamined: |
194 unexamined.add('/'.join([arch_file.arch, RUNNABLE_LD])) | 199 unexamined.add('/'.join([arch_file.arch, RUNNABLE_LD])) |
195 while unexamined: | 200 while unexamined: |
196 files_to_examine = {} | 201 files_to_examine = {} |
197 for arch_name in unexamined: | 202 for arch_name in unexamined: |
198 arch, name = arch_name.split('/') | 203 arch, name = arch_name.split('/') |
199 for path in self.FindLibsInPath(name): | 204 for path in self.FindLibsInPath(name): |
200 files_to_examine.setdefault(path, set()).add(arch) | 205 files_to_examine.setdefault(path, set()).add(arch) |
201 new_files, needed = self.GleanFromObjdump(files_to_examine) | 206 new_files, needed = self.GleanFromObjdump(files_to_examine) |
202 all_files.update(new_files) | 207 all_files.update(new_files) |
(...skipping 22 matching lines...) Expand all Loading... |
225 urllib.url2pathname(arch_file.url)) | 230 urllib.url2pathname(arch_file.url)) |
226 try: | 231 try: |
227 os.makedirs(os.path.dirname(destination)) | 232 os.makedirs(os.path.dirname(destination)) |
228 except OSError as exception_info: | 233 except OSError as exception_info: |
229 if exception_info.errno != errno.EEXIST: | 234 if exception_info.errno != errno.EEXIST: |
230 raise | 235 raise |
231 if (os.path.normcase(os.path.abspath(source)) != | 236 if (os.path.normcase(os.path.abspath(source)) != |
232 os.path.normcase(os.path.abspath(destination))): | 237 os.path.normcase(os.path.abspath(destination))): |
233 shutil.copy2(source, destination) | 238 shutil.copy2(source, destination) |
234 | 239 |
235 def _GenerateManifest(self): | 240 def _GenerateManifest(self, runnable=True): |
236 programs = {} | 241 '''Create a JSON formatted dict containing the files |
237 files = {} | 242 |
| 243 NaCl will map url requests based on architecture. The startup NEXE |
| 244 can always be found under the top key PROGRAM. Additional files are under |
| 245 the FILES key further mapped by file name. In the case of 'runnable' the |
| 246 PROGRAM key is populated with urls pointing the runnable-ld.so which acts |
| 247 as the startup nexe. The application itself, is then placed under the |
| 248 FILES key mapped as 'main.exe' instead of it's original name so that the |
| 249 loader can find it.''' |
| 250 manifest = { FILES_KEY: {}, PROGRAM_KEY: {} } |
| 251 needed = self.GetNeeded() |
238 | 252 |
239 def add_files(needed): | 253 for need in needed: |
240 for filename, arch_file in needed.items(): | 254 archinfo = needed[need] |
241 files.setdefault(arch_file.arch, set()).add(arch_file.name) | 255 urlinfo = { URL_KEY: archinfo.url } |
| 256 name = archinfo.name |
242 | 257 |
243 needed = self.GetNeeded() | 258 # If starting with runnable-ld.so, make that the main executable. |
244 add_files(needed) | 259 if runnable: |
| 260 if need.endswith(RUNNABLE_LD): |
| 261 manifest[PROGRAM_KEY][archinfo.arch] = urlinfo |
| 262 continue |
245 | 263 |
246 for filename in self.main_files: | 264 # For the main nexes: |
247 arch_file = needed[filename] | 265 if need.endswith('.nexe') and need in self.main_files: |
248 programs[arch_file.arch] = arch_file.name | 266 # Place it under program if we aren't using the runnable-ld.so. |
| 267 if not runnable: |
| 268 manifest[PROGRAM_KEY][archinfo.arch] = urlinfo |
| 269 continue |
| 270 # Otherwise, treat it like another another file named main.nexe. |
| 271 name = MAIN_NEXE |
249 | 272 |
250 filemap = {} | 273 fileinfo = manifest[FILES_KEY].get(name, {}) |
251 for arch in files: | 274 fileinfo[archinfo.arch] = urlinfo |
252 for file in files[arch]: | 275 manifest[FILES_KEY][name] = fileinfo |
253 if file not in programs.values() and file != RUNNABLE_LD: | |
254 filemap.setdefault(file, set()).add(arch) | |
255 | 276 |
256 def arch_name(arch, file): | |
257 # nmf files expect unix-style path separators | |
258 return {URL_KEY: '/'.join(self.lib_prefix + [arch, file])} | |
259 | |
260 # TODO(mcgrathr): perhaps notice a program with no deps | |
261 # (i.e. statically linked) and generate program=nexe instead? | |
262 manifest = {PROGRAM_KEY: {}, FILES_KEY: {MAIN_NEXE: {}}} | |
263 for arch in programs: | |
264 manifest[PROGRAM_KEY][arch] = arch_name(arch, RUNNABLE_LD) | |
265 manifest[FILES_KEY][MAIN_NEXE][arch] = {URL_KEY: programs[arch]} | |
266 | |
267 for file in filemap: | |
268 manifest[FILES_KEY][file] = dict([(arch, arch_name(arch, file)) | |
269 for arch in filemap[file]]) | |
270 self.manifest = manifest | 277 self.manifest = manifest |
271 | 278 |
272 def GetManifest(self): | 279 def GetManifest(self): |
273 '''Returns a JSON-formatted dict containing the NaCl dependencies''' | 280 '''Returns a JSON-formatted dict containing the NaCl dependencies''' |
274 if not self.manifest: | 281 if not self.manifest: |
275 self._GenerateManifest() | 282 self._GenerateManifest() |
276 | 283 |
277 return self.manifest | 284 return self.manifest |
278 | 285 |
279 def GetJson(self): | 286 def GetJson(self): |
(...skipping 14 matching lines...) Expand all Loading... |
294 parser.add_option('-D', '--objdump', dest='objdump', default='objdump', | 301 parser.add_option('-D', '--objdump', dest='objdump', default='objdump', |
295 help='Use TOOL as the "objdump" tool to run', | 302 help='Use TOOL as the "objdump" tool to run', |
296 metavar='TOOL') | 303 metavar='TOOL') |
297 parser.add_option('-L', '--library-path', dest='lib_path', | 304 parser.add_option('-L', '--library-path', dest='lib_path', |
298 action='append', default=[], | 305 action='append', default=[], |
299 help='Add DIRECTORY to library search path', | 306 help='Add DIRECTORY to library search path', |
300 metavar='DIRECTORY') | 307 metavar='DIRECTORY') |
301 parser.add_option('-s', '--stage-dependencies', dest='stage_dependencies', | 308 parser.add_option('-s', '--stage-dependencies', dest='stage_dependencies', |
302 help='Destination directory for staging libraries', | 309 help='Destination directory for staging libraries', |
303 metavar='DIRECTORY') | 310 metavar='DIRECTORY') |
| 311 parser.add_option('-r', '--remove', dest='remove', |
| 312 help='Remove the prefix from the files.', |
| 313 metavar='PATH') |
304 (options, args) = parser.parse_args(argv) | 314 (options, args) = parser.parse_args(argv) |
305 | 315 |
306 if len(args) < 1: | 316 if len(args) < 1: |
307 parser.print_usage() | 317 parser.print_usage() |
308 sys.exit(1) | 318 sys.exit(1) |
309 | 319 |
310 nmf = NmfUtils(objdump=options.objdump, | 320 nmf = NmfUtils(objdump=options.objdump, |
311 main_files=args, | 321 main_files=args, |
312 lib_path=options.lib_path) | 322 lib_path=options.lib_path) |
313 | 323 |
314 manifest = nmf.GetManifest() | 324 manifest = nmf.GetManifest() |
315 | 325 |
316 if options.output is None: | 326 if options.output is None: |
317 sys.stdout.write(nmf.GetJson()) | 327 sys.stdout.write(nmf.GetJson()) |
318 else: | 328 else: |
319 with open(options.output, 'w') as output: | 329 with open(options.output, 'w') as output: |
320 output.write(nmf.GetJson()) | 330 output.write(nmf.GetJson()) |
321 | 331 |
322 if options.stage_dependencies: | 332 if options.stage_dependencies: |
323 nmf.StageDependencies(options.stage_dependencies) | 333 nmf.StageDependencies(options.stage_dependencies) |
324 | 334 |
325 | 335 |
326 # Invoke this file directly for simple testing. | 336 # Invoke this file directly for simple testing. |
327 if __name__ == '__main__': | 337 if __name__ == '__main__': |
328 sys.exit(Main(sys.argv[1:])) | 338 sys.exit(Main(sys.argv[1:])) |
OLD | NEW |