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