Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(180)

Side by Side Diff: mojo/nacl/sfi/nacl_bindings_generator/generate_nacl_bindings.py

Issue 2051163002: Nuke NaCl SFI, part 1. (Closed) Base URL: https://github.com/domokit/mojo.git@master
Patch Set: Created 4 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 #!/usr/bin/python
2 # Copyright 2014 The Chromium 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 # pylint: disable=W0104,W0106,F0401,R0201
7
8 import errno
9 import os.path
10 import sys
11
12 import interface
13
14
15 def ScriptDir():
16 return os.path.dirname(os.path.abspath(__file__))
17
18
19 def RepoRoot():
20 # Go up 4 levels, to get out of "mojo/nacl/sfi/nacl_bindings_generator".
21 return os.path.dirname(os.path.dirname(os.path.dirname(
22 os.path.dirname(ScriptDir()))))
23
24
25 def _AddThirdPartyImportPath():
26 sys.path.append(os.path.join(RepoRoot(), 'third_party'))
27
28
29 _AddThirdPartyImportPath()
30 import jinja2
31
32 loader = jinja2.FileSystemLoader(ScriptDir())
33 jinja_env = jinja2.Environment(loader=loader, keep_trailing_newline=True)
34
35
36 # Accumulate lines of code with varying levels of indentation.
37 class CodeWriter(object):
38 def __init__(self):
39 self._lines = []
40 self._margin = ''
41 self._margin_stack = []
42
43 def __lshift__(self, line):
44 self._lines.append((self._margin + line).rstrip())
45
46 def PushMargin(self):
47 self._margin_stack.append(self._margin)
48 self._margin += ' '
49
50 def PopMargin(self):
51 self._margin = self._margin_stack.pop()
52
53 def GetValue(self):
54 return '\n'.join(self._lines).rstrip() + '\n'
55
56 def Indent(self):
57 return Indent(self)
58
59
60 # Context handler that automatically indents and dedents a CodeWriter
61 class Indent(object):
62 def __init__(self, writer):
63 self._writer = writer
64
65 def __enter__(self):
66 self._writer.PushMargin()
67
68 def __exit__(self, type_, value, traceback):
69 self._writer.PopMargin()
70
71
72 def TemplateFile(name):
73 return os.path.join(os.path.dirname(__file__), name)
74
75
76 # Wraps comma separated lists as needed.
77 # TODO(teravest): Eliminate Wrap() and use "git cl format" when code is checked
78 # in.
79 def Wrap(pre, items, post):
80 complete = pre + ', '.join(items) + post
81 if len(complete) <= 80:
82 return [complete]
83 lines = [pre]
84 indent = ' '
85 for i, item in enumerate(items):
86 if i < len(items) - 1:
87 lines.append(indent + item + ',')
88 else:
89 lines.append(indent + item + post)
90 return lines
91
92
93 def GeneratorWarning():
94 return ('// WARNING this file was generated by %s\n// Do not edit by hand.' %
95 os.path.basename(__file__))
96
97
98 # Untrusted library which thunks from the public Mojo API to the IRT interface
99 # implementing the public Mojo API.
100 def GenerateLibMojo(functions, common_vars, out):
101 template = jinja_env.get_template('libmojo.cc.tmpl')
102
103 code = CodeWriter()
104
105 for f in functions:
106 for line in Wrap('%s %s(' % (f.return_type, f.name), f.ParamList(), ') {'):
107 code << line
108
109 with code.Indent():
110 code << 'struct nacl_irt_mojo* irt_mojo = get_irt_mojo();'
111 code << 'if (!irt_mojo)'
112 with code.Indent():
113 code << 'abort();'
114 code << 'return irt_mojo->%s(%s);' % (
115 f.name, ', '.join([p.name for p in f.params]))
116
117 code << '}'
118 code << ''
119
120 body = code.GetValue()
121 text = template.render(
122 body=body,
123 **common_vars)
124 out.write(text)
125
126
127 # Parameters passed into trusted code are handled differently depending on
128 # details of the parameter. ParamImpl instances encapsulate these differences
129 # and are used to generate the code that transfers parameters across the
130 # untrusted/trusted boundary.
131 class ParamImpl(object):
132 def __init__(self, param):
133 self.param = param
134
135 # Declare whatever variables are needed to handle this particular parameter.
136 def DeclareVars(self, code):
137 raise NotImplementedError(type(self))
138
139 # Convert the untrusted representation of the parameter into a trusted
140 # representation, such as a scalar value or a trusted pointer into the
141 # untrusted address space.
142 def ConvertParam(self):
143 raise NotImplementedError(type(self))
144
145 # For this particular parameter, what expression should be passed when
146 # invoking the trusted Mojo API function?
147 def CallParam(self):
148 raise NotImplementedError(type(self))
149
150 # After invoking the trusted Mojo API function, transfer data back into
151 # untrusted memory. Overriden for Out and InOut parameters.
152 def CopyOut(self, code):
153 pass
154
155 # Converting array parameters needs to be defered until after the scalar
156 # parameter containing the size of the array has itself been converted.
157 def IsArray(self):
158 return False
159
160
161 class ScalarInputImpl(ParamImpl):
162 def DeclareVars(self, code):
163 code << '%s %s_value;' % (self.param.base_type, self.param.name)
164
165 def ConvertParam(self):
166 p = self.param
167 return ('ConvertScalarInput(nap, params[%d], &%s_value)' %
168 (p.uid + 1, p.name))
169
170 def CallParam(self):
171 return '%s_value' % self.param.name
172
173
174 class ScalarOutputImpl(ParamImpl):
175 def DeclareVars(self, code):
176 code << '%s volatile* %s_ptr;' % (self.param.base_type, self.param.name)
177 code << '%s %s_value;' % (self.param.base_type, self.param.name)
178
179 def ConvertParam(self):
180 p = self.param
181 return ('ConvertScalarOutput(nap, params[%d], %s, &%s_ptr)' %
182 (p.uid + 1, CBool(p.is_optional), p.name))
183
184 def CallParam(self):
185 name = self.param.name
186 expr = '&%s_value' % name
187 if self.param.is_optional:
188 expr = '%s_ptr ? %s : nullptr' % (name, expr)
189 return expr
190
191 def CopyOut(self, code):
192 name = self.param.name
193 if self.param.is_struct:
194 # C++ errors when you try to copy a volatile struct pointer.
195 # (There are no default copy constructors for this case.)
196 # memcpy instead.
197 copy_stmt = ('memcpy_volatile_out(%s_ptr, &%s_value, sizeof(%s));' %
198 (name, name, self.param.base_type))
199 else:
200 copy_stmt = '*%s_ptr = %s_value;' % (name, name)
201
202 if self.param.is_optional:
203 code << 'if (%s_ptr) {' % (name)
204 with code.Indent():
205 code << copy_stmt
206 code << '}'
207 else:
208 code << copy_stmt
209
210
211 class ScalarInOutImpl(ParamImpl):
212 def DeclareVars(self, code):
213 code << '%s volatile* %s_ptr;' % (self.param.base_type, self.param.name)
214 code << '%s %s_value;' % (self.param.base_type, self.param.name)
215
216 def ConvertParam(self):
217 p = self.param
218 return ('ConvertScalarInOut(nap, params[%d], %s, &%s_value, &%s_ptr)' %
219 (p.uid + 1, CBool(p.is_optional), p.name, p.name))
220
221 def CallParam(self):
222 name = self.param.name
223 expr = '&%s_value' % name
224 if self.param.is_optional:
225 expr = '%s_ptr ? %s : nullptr' % (name, expr)
226 return expr
227
228 def CopyOut(self, code):
229 name = self.param.name
230 if self.param.is_optional:
231 code << 'if (%s_ptr) {' % (name)
232 with code.Indent():
233 code << '*%s_ptr = %s_value;' % (name, name)
234 code << '}'
235 else:
236 code << '*%s_ptr = %s_value;' % (name, name)
237
238
239 class PointerInputImpl(ParamImpl):
240 def DeclareVars(self, code):
241 code << '%s %s_value;' % (self.param.base_type, self.param.name)
242
243 def ConvertParam(self):
244 p = self.param
245 return ('ConvertPointerInput(nap, params[%d], &%s_value)' %
246 (p.uid + 1, p.name))
247
248 def CallParam(self):
249 return '%s_value' % self.param.name
250
251
252 class PointerInOutImpl(ParamImpl):
253 def DeclareVars(self, code):
254 code << 'uint32_t volatile* %s_ptr;' % (self.param.name,)
255 code << '%s %s_value;' % (self.param.base_type, self.param.name)
256
257 def ConvertParam(self):
258 p = self.param
259 return ('ConvertPointerInOut(nap, params[%d], %s, &%s_value, &%s_ptr)' %
260 (p.uid + 1, CBool(p.is_optional), p.name, p.name))
261
262 def CallParam(self):
263 name = self.param.name
264 expr = '&%s_value' % name
265 if self.param.is_optional:
266 expr = '%s_ptr ? %s : nullptr' % (name, expr)
267 return expr
268
269 def CopyOut(self, code):
270 assert not self.param.is_optional
271 name = self.param.name
272 if self.param.is_optional:
273 code << 'if (%s_ptr) {' % (name)
274 with code.Indent():
275 code << 'CopyOutPointer(nap, %s_value, %s_ptr);' % (name, name)
276 code << '}'
277 else:
278 code << 'CopyOutPointer(nap, %s_value, %s_ptr);' % (name, name)
279
280
281 class ArrayImpl(ParamImpl):
282 def DeclareVars(self, code):
283 code << '%s %s;' % (self.param.param_type, self.param.name)
284
285 def ConvertParam(self):
286 p = self.param
287 if p.base_type == 'void':
288 element_size = '1'
289 else:
290 element_size = 'sizeof(*%s)' % p.name
291
292 return ('ConvertArray(nap, params[%d], %s, %s, %s, &%s)' %
293 (p.uid + 1, p.size + '_value', element_size, CBool(p.is_optional),
294 p.name))
295
296 def CallParam(self):
297 return self.param.name
298
299 def IsArray(self):
300 return True
301
302
303 class ExtensibleStructInputImpl(ParamImpl):
304 def DeclareVars(self, code):
305 code << '%s %s;' % (self.param.param_type, self.param.name)
306
307 def ConvertParam(self):
308 p = self.param
309 return ('ConvertExtensibleStructInput(nap, params[%d], %s, &%s)' %
310 (p.uid + 1, CBool(p.is_optional), p.name))
311
312 def CallParam(self):
313 return self.param.name
314
315
316 # We can handle extensible out structs just like we handle output arrays, except
317 # that the (input buffer) size is always in bytes.
318 class ExtensibleStructOutputImpl(ParamImpl):
319 def DeclareVars(self, code):
320 code << '%s %s;' % (self.param.param_type, self.param.name)
321
322 def ConvertParam(self):
323 p = self.param
324 return ('ConvertArray(nap, params[%d], %s, %s, %s, &%s)' %
325 (p.uid + 1, p.size + '_value', '1', CBool(p.is_optional), p.name))
326
327 def CallParam(self):
328 return self.param.name
329
330 def IsArray(self):
331 return True
332
333
334 def ImplForParam(p):
335 if p.IsScalar():
336 if p.is_output:
337 if p.is_pointer:
338 return PointerInOutImpl(p)
339 elif p.is_input:
340 return ScalarInOutImpl(p)
341 else:
342 if p.is_always_written:
343 return ScalarOutputImpl(p)
344 else:
345 # Mojo defines that some of its outputs will not be set in specific
346 # cases. To avoid the complexity of determining if the output was set
347 # by Mojo, copy the output's current value (possibly junk) and copy it
348 # back to untrusted memory afterwards.
349 return ScalarInOutImpl(p)
350 else:
351 if p.is_pointer:
352 return PointerInputImpl(p)
353 else:
354 return ScalarInputImpl(p)
355 elif p.is_array:
356 return ArrayImpl(p)
357 elif p.is_struct:
358 if p.is_input and not p.is_output and p.is_extensible:
359 return ExtensibleStructInputImpl(p)
360 if p.is_output and not p.is_input and p.is_extensible:
361 return ExtensibleStructOutputImpl(p)
362 if not p.is_input and p.is_output and not p.is_extensible:
363 return ScalarOutputImpl(p)
364 assert False, p.name
365
366
367 def CBool(value):
368 return 'true' if value else 'false'
369
370
371 # A trusted wrapper that validates the arguments passed from untrusted code
372 # before passing them to the underlying public Mojo API.
373 def GenerateMojoSyscall(functions, common_vars, out):
374 template = jinja_env.get_template('mojo_syscall.cc.tmpl')
375
376 code = CodeWriter()
377 code.PushMargin()
378
379 for f in functions:
380 is_implemented = not f.broken_in_nacl
381
382 impls = [ImplForParam(p) for p in f.params]
383 impls.append(ImplForParam(f.result_param))
384
385 code << 'case %d:' % f.uid
386
387 if not is_implemented:
388 with code.Indent():
389 code << 'fprintf(stderr, "%s not implemented\\n");' % f.name
390 code << 'return -1;'
391 continue
392
393 code.PushMargin()
394
395 code << '{'
396
397 with code.Indent():
398 num_params = len(f.params) + 2
399 code << 'if (num_params != %d) {' % num_params
400 with code.Indent():
401 code << 'return -1;'
402 code << '}'
403
404 # Declare temporaries.
405 for impl in impls:
406 impl.DeclareVars(code)
407
408 def ConvertParam(code, impl):
409 code << 'if (!%s) {' % impl.ConvertParam()
410 with code.Indent():
411 code << 'return -1;'
412 code << '}'
413
414 code << '{'
415 with code.Indent():
416 code << 'ScopedCopyLock copy_lock(nap);'
417 # Convert and validate pointers in two passes.
418 # Arrays cannot be validated until the size parameter has been
419 # converted.
420 for impl in impls:
421 if not impl.IsArray():
422 ConvertParam(code, impl)
423 for impl in impls:
424 if impl.IsArray():
425 ConvertParam(code, impl)
426 code << '}'
427 code << ''
428
429 # Call
430 getParams = [impl.CallParam() for impl in impls[:-1]]
431 callTarget = f.name
432 # Redirect to namespaced functions.
433 if callTarget.startswith("Mojo"):
434 callTarget = callTarget[:4] + 'SystemImpl' + callTarget[4:]
435 getParams = ['g_mojo_system'] + getParams
436 code << 'result_value = %s(%s);' % (callTarget, ', '.join(getParams))
437 code << ''
438
439 # Write outputs
440 code << '{'
441 with code.Indent():
442 code << 'ScopedCopyLock copy_lock(nap);'
443 for impl in impls:
444 impl.CopyOut(code)
445 code << '}'
446 code << ''
447
448 code << 'return 0;'
449 code << '}'
450
451 code.PopMargin()
452
453 body = code.GetValue()
454 text = template.render(
455 body=body,
456 **common_vars)
457 out.write(text)
458
459
460 # A header declaring the IRT interface for accessing Mojo functions.
461 def GenerateMojoIrtHeader(functions, common_vars, out):
462 template = jinja_env.get_template('mojo_irt.h.tmpl')
463 code = CodeWriter()
464
465 code << 'struct nacl_irt_mojo {'
466 with code.Indent():
467 for f in functions:
468 for line in Wrap('%s (*%s)(' % (f.return_type, f.name),
469 f.ParamList(),
470 ');'):
471 code << line
472
473 code << '};'
474
475 body = code.GetValue()
476
477 text = template.render(
478 body=body,
479 **common_vars)
480 out.write(text)
481
482 # IRT interface which implements the Mojo public API.
483 def GenerateMojoIrtImplementation(functions, common_vars, out):
484 template = jinja_env.get_template('mojo_irt.c.tmpl')
485 code = CodeWriter()
486
487 for f in functions:
488 for line in Wrap('static %s irt_%s(' % (f.return_type, f.name),
489 f.ParamList(),
490 ') {'):
491 code << line
492
493 # 2 extra parameters: message ID and return value.
494 num_params = len(f.params) + 2
495
496 with code.Indent():
497 code << 'uint32_t params[%d];' % num_params
498 return_type = f.result_param.base_type
499 if return_type == 'MojoResult':
500 default = 'MOJO_RESULT_INVALID_ARGUMENT'
501 elif return_type == 'MojoTimeTicks':
502 default = '0'
503 else:
504 raise Exception('Unhandled return type: ' + return_type)
505 code << '%s %s = %s;' % (return_type, f.result_param.name, default)
506
507 # Message ID
508 code << 'params[0] = %d;' % f.uid
509 # Parameter pointers
510 cast_template = 'params[%d] = (uint32_t)(%s);'
511 for p in f.params:
512 ptr = p.name
513 if p.IsPassedByValue():
514 ptr = '&' + ptr
515 code << cast_template % (p.uid + 1, ptr)
516 # Return value pointer
517 code << cast_template % (num_params - 1, '&' + f.result_param.name)
518
519 code << 'DoMojoCall(params, sizeof(params));'
520 code << 'return %s;' % f.result_param.name
521
522 # Add body here.
523 code << "};"
524 code << "\n"
525
526 # Now we've emitted all the functions, but we still need the struct
527 # definition.
528 code << 'struct nacl_irt_mojo kIrtMojo = {'
529 for f in functions:
530 with code.Indent():
531 code << '&irt_%s,' % f.name
532 code << '};'
533
534 body = code.GetValue()
535
536 text = template.render(
537 body=body,
538 **common_vars)
539 out.write(text)
540
541
542 def OutFile(dir_path, name):
543 if not os.path.exists(dir_path):
544 try:
545 os.makedirs(dir_path)
546 except OSError as e:
547 # There may have been a race to create this directory.
548 if e.errno != errno.EEXIST:
549 raise
550 full_path = os.path.join(dir_path, name)
551 print full_path
552 return open(full_path, 'w')
553
554
555 def main():
556 root_dir = RepoRoot()
557
558 platform_dir = 'mojo/public/platform/nacl'
559 bindings_dir = 'mojo/nacl/sfi/nacl_bindings'
560
561 full_platform_dir = os.path.join(root_dir, platform_dir)
562 full_bindings_dir = os.path.join(root_dir, bindings_dir)
563
564 common_vars = dict(
565 generator_warning=GeneratorWarning(),
566 platform_dir=platform_dir,
567 platform_dir_header_path=platform_dir.replace("/", "_").upper(),
568 bindings_dir=bindings_dir,
569 )
570
571 mojo = interface.MakeInterface()
572
573 out = OutFile(full_platform_dir, 'libmojo.cc')
574 GenerateLibMojo(mojo.functions, common_vars, out)
575
576 out = OutFile(full_bindings_dir, 'mojo_syscall.cc')
577 GenerateMojoSyscall(mojo.functions, common_vars, out)
578
579 out = OutFile(full_platform_dir, 'mojo_irt.h')
580 GenerateMojoIrtHeader(mojo.functions, common_vars, out)
581
582 out = OutFile(full_bindings_dir, 'mojo_irt.c')
583 GenerateMojoIrtImplementation(mojo.functions, common_vars, out)
584
585 if __name__ == '__main__':
586 main()
OLDNEW
« no previous file with comments | « mojo/nacl/sfi/nacl_bindings/monacl_sel_main.cc ('k') | mojo/nacl/sfi/nacl_bindings_generator/interface.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698