1# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- 2# vim: set filetype=python: 3# This Source Code Form is subject to the terms of the Mozilla Public 4# License, v. 2.0. If a copy of the MPL was not distributed with this 5# file, You can obtain one at http://mozilla.org/MPL/2.0/. 6 7from __future__ import print_function 8 9from copy import deepcopy 10from six import iteritems 11from struct import unpack 12import os 13from uuid import UUID 14 15H_HEADER = """/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 16/* vim: set ts=8 sts=2 et sw=2 tw=80: */ 17/* This file was auto-generated from {0} by gen_dll_blocklist_data.py. */ 18 19#ifndef mozilla_{1}_h 20#define mozilla_{1}_h 21 22""" 23 24H_FOOTER = """#endif // mozilla_{1}_h 25 26""" 27 28H_DEFS_BEGIN_DEFAULT = """#include "mozilla/WindowsDllBlocklistCommon.h" 29 30DLL_BLOCKLIST_DEFINITIONS_BEGIN 31 32""" 33 34H_DEFS_END_DEFAULT = """ 35DLL_BLOCKLIST_DEFINITIONS_END 36 37""" 38 39H_BEGIN_LSP = """#include <guiddef.h> 40 41static const GUID gLayerGuids[] = { 42 43""" 44 45H_END_LSP = """ 46}; 47 48""" 49 50H_BEGIN_A11Y = """#include "mozilla/WindowsDllBlocklistCommon.h" 51 52DLL_BLOCKLIST_DEFINITIONS_BEGIN_NAMED(gBlockedInprocDlls) 53 54""" 55 56# These flag names should match the ones defined in WindowsDllBlocklistCommon.h 57FLAGS_DEFAULT = "FLAGS_DEFAULT" 58BLOCK_WIN8_AND_OLDER = "BLOCK_WIN8_AND_OLDER" 59BLOCK_WIN7_AND_OLDER = "BLOCK_WIN7_AND_OLDER" 60USE_TIMESTAMP = "USE_TIMESTAMP" 61CHILD_PROCESSES_ONLY = "CHILD_PROCESSES_ONLY" 62BROWSER_PROCESS_ONLY = "BROWSER_PROCESS_ONLY" 63SUBSTITUTE_LSP_PASSTHROUGH = "SUBSTITUTE_LSP_PASSTHROUGH" 64REDIRECT_TO_NOOP_ENTRYPOINT = "REDIRECT_TO_NOOP_ENTRYPOINT" 65 66# Only these flags are available in the input script 67INPUT_ONLY_FLAGS = { 68 BLOCK_WIN8_AND_OLDER, 69 BLOCK_WIN7_AND_OLDER, 70} 71 72 73def FILTER_ALLOW_ALL(entry): 74 # A11y entries are special, so we always exclude those by default 75 # (so it's not really allowing 'all', but it is simpler to reason about by 76 # pretending that it is allowing all.) 77 return not isinstance(entry, A11yBlocklistEntry) 78 79 80def FILTER_DENY_ALL(entry): 81 return False 82 83 84def FILTER_ALLOW_ONLY_A11Y(entry): 85 return isinstance(entry, A11yBlocklistEntry) 86 87 88def FILTER_ALLOW_ONLY_LSP(entry): 89 return isinstance(entry, LspBlocklistEntry) 90 91 92def FILTER_TESTS_ONLY(entry): 93 return not isinstance(entry, A11yBlocklistEntry) and entry.is_test() 94 95 96def derive_test_key(key): 97 return key + "_TESTS" 98 99 100ALL_DEFINITION_LISTS = ("ALL_PROCESSES", "BROWSER_PROCESS", "CHILD_PROCESSES") 101 102 103class BlocklistDescriptor(object): 104 """This class encapsulates every file that is output from this script. 105 Each instance has a name, an "input specification", and optional "flag 106 specification" and "output specification" entries. 107 """ 108 109 DEFAULT_OUTSPEC = { 110 "mode": "", 111 "filter": FILTER_ALLOW_ALL, 112 "begin": H_DEFS_BEGIN_DEFAULT, 113 "end": H_DEFS_END_DEFAULT, 114 } 115 116 FILE_NAME_TPL = "WindowsDllBlocklist{0}Defs" 117 118 OutputDir = None 119 ExistingFd = None 120 ExistingFdLeafName = None 121 122 def __init__(self, name, inspec, **kwargs): 123 """Positional arguments: 124 125 name -- String containing the name of the output list. 126 127 inspec -- One or more members of ALL_DEFINITION_LISTS. The input used 128 for this blocklist file is the union of all lists specified by this 129 variable. 130 131 Keyword arguments: 132 133 outspec -- an optional list of dicts that specify how the lists output 134 will be written out to a file. Each dict may contain the following keys: 135 136 'mode' -- a string that specifies a mode that is used when writing 137 out list entries to this particular output. This is passed in as the 138 mode argument to DllBlocklistEntry's write method. 139 140 'filter' -- a function that, given a blocklist entry, decides 141 whether or not that entry shall be included in this output file. 142 143 'begin' -- a string that is written to the output file after writing 144 the file's header, but prior to writing out any blocklist entries. 145 146 'end' -- a string that is written to the output file after writing 147 out any blocklist entries but before the file's footer. 148 149 Any unspecified keys will be assigned default values. 150 151 flagspec -- an optional dict whose keys consist of one or more of the 152 list names from inspec. Each corresponding value is a set of flags that 153 should be applied to each entry from that list. For example, the 154 flagspec: 155 156 {'CHILD_PROCESSES': {CHILD_PROCESSES_ONLY}} 157 158 causes any blocklist entries from the CHILD_PROCESSES list to be output 159 with the CHILD_PROCESSES_ONLY flag set. 160 161 """ 162 163 self._name = name 164 165 # inspec's elements must all come from ALL_DEFINITION_LISTS 166 assert not (set(inspec).difference(set(ALL_DEFINITION_LISTS))) 167 168 # Internally to the descriptor, we store input specifications as a dict 169 # that maps each input blocklist name to the set of flags to be applied 170 # to each entry in that blocklist. 171 self._inspec = {blocklist: set() for blocklist in inspec} 172 173 self._outspecs = kwargs.get("outspec", BlocklistDescriptor.DEFAULT_OUTSPEC) 174 if isinstance(self._outspecs, dict): 175 # _outspecs should always be stored as a list of dicts 176 self._outspecs = [self._outspecs] 177 178 flagspecs = kwargs.get("flagspec", dict()) 179 # flagspec's keys must all come from ALL_DEFINITION_LISTS 180 assert not (set(flagspecs.keys()).difference(set(self._inspec.keys()))) 181 182 # Merge the flags from flagspec into _inspec's sets 183 for blocklist, flagspec in iteritems(flagspecs): 184 spec = self._inspec[blocklist] 185 if not isinstance(spec, set): 186 raise TypeError("Flag spec for list %s must be a set!" % blocklist) 187 spec.update(flagspec) 188 189 @staticmethod 190 def set_output_fd(fd): 191 """The build system has already provided an open file descriptor for 192 one of our outputs. We save that here so that we may use that fd once 193 we're ready to process that fd's file. We also obtain the output dir for 194 use as the base directory for the other output files that we open. 195 """ 196 BlocklistDescriptor.ExistingFd = fd 197 ( 198 BlocklistDescriptor.OutputDir, 199 BlocklistDescriptor.ExistingFdLeafName, 200 ) = os.path.split(fd.name) 201 202 @staticmethod 203 def ensure_no_dupes(defs): 204 """Ensure that defs does not contain any dupes. We raise a ValueError 205 because this is a bug in the input and requires developer intervention. 206 """ 207 seen = set() 208 for e in defs: 209 name = e.get_name() 210 if name not in seen: 211 seen.add(name) 212 else: 213 raise ValueError("Duplicate entry found: %s" % name) 214 215 @staticmethod 216 def get_test_entries(exec_env, blocklist): 217 """Obtains all test entries for the corresponding blocklist, and also 218 ensures that each entry has its test flag set. 219 220 Positional arguments: 221 222 exec_env -- dict containing the globals that were passed to exec to 223 when the input script was run. 224 225 blocklist -- The name of the blocklist to obtain tests from. This 226 should be one of the members of ALL_DEFINITION_LISTS 227 """ 228 test_key = derive_test_key(blocklist) 229 230 def set_is_test(elem): 231 elem.set_is_test() 232 return elem 233 234 return map(set_is_test, exec_env[test_key]) 235 236 def gen_list(self, exec_env, filter_func): 237 """Generates a sorted list of blocklist entries from the input script, 238 filtered via filter_func. 239 240 Positional arguments: 241 242 exec_env -- dict containing the globals that were passed to exec to 243 when the input script was run. This function expects exec_env to 244 contain lists of blocklist entries, keyed using one of the members of 245 ALL_DEFINITION_LISTS. 246 247 filter_func -- a filter function that evaluates each blocklist entry 248 to determine whether or not it should be included in the results. 249 """ 250 251 # This list contains all the entries across all blocklists that we 252 # potentially want to process 253 unified_list = [] 254 255 # For each blocklist specified in the _inspec, we query the globals 256 # for their entries, add any flags, and then add them to the 257 # unified_list. 258 for blocklist, listflags in iteritems(self._inspec): 259 260 def add_list_flags(elem): 261 # We deep copy so that flags set for an entry in one blocklist 262 # do not interfere with flags set for an entry in a different 263 # list. 264 result = deepcopy(elem) 265 result.add_flags(listflags) 266 return result 267 268 # We add list flags *before* filtering because the filters might 269 # want to access flags as part of their operation. 270 unified_list.extend(map(add_list_flags, exec_env[blocklist])) 271 272 # We also add any test entries for the lists specified by _inspec 273 unified_list.extend( 274 map(add_list_flags, self.get_test_entries(exec_env, blocklist)) 275 ) 276 277 # There should be no dupes in the input. If there are, raise an error. 278 self.ensure_no_dupes(unified_list) 279 280 # Now we filter out any unwanted list entries 281 filtered_list = filter(filter_func, unified_list) 282 283 # Sort the list on entry name so that the blocklist code may use 284 # binary search if it so chooses. 285 return sorted(filtered_list, key=lambda e: e.get_name()) 286 287 @staticmethod 288 def get_fd(outspec_leaf_name): 289 """If BlocklistDescriptor.ExistingFd corresponds to outspec_leaf_name, 290 then we return that. Otherwise, we construct a new absolute path to 291 outspec_leaf_name and open a new file descriptor for writing. 292 """ 293 if ( 294 not BlocklistDescriptor.ExistingFd 295 or BlocklistDescriptor.ExistingFdLeafName != outspec_leaf_name 296 ): 297 new_name = os.path.join(BlocklistDescriptor.OutputDir, outspec_leaf_name) 298 return open(new_name, "w") 299 300 fd = BlocklistDescriptor.ExistingFd 301 BlocklistDescriptor.ExistingFd = None 302 return fd 303 304 def write(self, src_file, exec_env): 305 """Write out all output files that are specified by this descriptor. 306 307 Positional arguments: 308 309 src_file -- name of the input file from which the lists were generated. 310 311 exec_env -- dictionary containing the lists that were parsed from the 312 input file when it was executed. 313 """ 314 315 for outspec in self._outspecs: 316 # Use DEFAULT_OUTSPEC to supply defaults for any unused outspec keys 317 effective_outspec = BlocklistDescriptor.DEFAULT_OUTSPEC.copy() 318 effective_outspec.update(outspec) 319 320 entries = self.gen_list(exec_env, effective_outspec["filter"]) 321 if not entries: 322 continue 323 324 mode = effective_outspec["mode"] 325 326 # Since each output descriptor may generate output across multiple 327 # modes, each list is uniquified via the concatenation of our name 328 # and the mode. 329 listname = self._name + mode 330 leafname_no_ext = BlocklistDescriptor.FILE_NAME_TPL.format(listname) 331 leafname = leafname_no_ext + ".h" 332 333 with self.get_fd(leafname) as output: 334 print(H_HEADER.format(src_file, leafname_no_ext), file=output, end="") 335 print(effective_outspec["begin"], file=output, end="") 336 337 for e in entries: 338 e.write(output, mode) 339 340 print(effective_outspec["end"], file=output, end="") 341 print(H_FOOTER.format(src_file, leafname_no_ext), file=output, end="") 342 343 344A11Y_OUTPUT_SPEC = { 345 "filter": FILTER_ALLOW_ONLY_A11Y, 346 "begin": H_BEGIN_A11Y, 347} 348 349LSP_MODE_GUID = "Guid" 350 351LSP_OUTPUT_SPEC = [ 352 { 353 "mode": LSP_MODE_GUID, 354 "filter": FILTER_ALLOW_ONLY_LSP, 355 "begin": H_BEGIN_LSP, 356 "end": H_END_LSP, 357 }, 358] 359 360GENERATED_BLOCKLIST_FILES = [ 361 BlocklistDescriptor("A11y", ["BROWSER_PROCESS"], outspec=A11Y_OUTPUT_SPEC), 362 BlocklistDescriptor( 363 "Launcher", 364 ALL_DEFINITION_LISTS, 365 flagspec={ 366 "BROWSER_PROCESS": {BROWSER_PROCESS_ONLY}, 367 "CHILD_PROCESSES": {CHILD_PROCESSES_ONLY}, 368 }, 369 ), 370 BlocklistDescriptor( 371 "Legacy", 372 ALL_DEFINITION_LISTS, 373 flagspec={ 374 "BROWSER_PROCESS": {BROWSER_PROCESS_ONLY}, 375 "CHILD_PROCESSES": {CHILD_PROCESSES_ONLY}, 376 }, 377 ), 378 # Roughed-in for the moment; we'll enable this in bug 1238735 379 # BlocklistDescriptor('LSP', ALL_DEFINITION_LISTS, outspec=LSP_OUTPUT_SPEC), 380 BlocklistDescriptor( 381 "Test", ALL_DEFINITION_LISTS, outspec={"filter": FILTER_TESTS_ONLY} 382 ), 383] 384 385 386class PETimeStamp(object): 387 def __init__(self, ts): 388 max_timestamp = (2 ** 32) - 1 389 if ts < 0 or ts > max_timestamp: 390 raise ValueError("Invalid timestamp value") 391 self._value = ts 392 393 def __str__(self): 394 return "0x%08XU" % self._value 395 396 397class Version(object): 398 """Encapsulates a DLL version.""" 399 400 ALL_VERSIONS = 0xFFFFFFFFFFFFFFFF 401 UNVERSIONED = 0 402 403 def __init__(self, *args): 404 """There are multiple ways to construct a Version: 405 406 As a tuple containing four elements (recommended); 407 As four integral arguments; 408 As a PETimeStamp; 409 As a long integer. 410 411 The tuple and list formats require the value of each element to be 412 between 0 and 0xFFFF, inclusive. 413 """ 414 415 self._ver = Version.UNVERSIONED 416 417 if len(args) == 1: 418 if isinstance(args[0], tuple): 419 self.validate_iterable(args[0]) 420 421 self._ver = "MAKE_VERSION%r" % (args[0],) 422 elif isinstance(args[0], PETimeStamp): 423 self._ver = args[0] 424 else: 425 self._ver = int(args[0]) 426 elif len(args) == 4: 427 self.validate_iterable(args) 428 429 self._ver = "MAKE_VERSION%r" % (tuple(args),) 430 else: 431 raise ValueError("Bad arguments to Version constructor") 432 433 def validate_iterable(self, arg): 434 if len(arg) != 4: 435 raise ValueError("Versions must be a 4-tuple") 436 437 for component in arg: 438 if not isinstance(component, int) or component < 0 or component > 0xFFFF: 439 raise ValueError( 440 "Each version component must be a 16-bit " "unsigned integer" 441 ) 442 443 def build_long(self, args): 444 self.validate_iterable(args) 445 return ( 446 (int(args[0]) << 48) 447 | (int(args[1]) << 32) 448 | (int(args[2]) << 16) 449 | int(args[3]) 450 ) 451 452 def is_timestamp(self): 453 return isinstance(self._ver, PETimeStamp) 454 455 def __str__(self): 456 if isinstance(self._ver, int): 457 if self._ver == Version.ALL_VERSIONS: 458 return "DllBlockInfo::ALL_VERSIONS" 459 460 if self._ver == Version.UNVERSIONED: 461 return "DllBlockInfo::UNVERSIONED" 462 463 return "0x%016XULL" % self._ver 464 465 return str(self._ver) 466 467 468class DllBlocklistEntry(object): 469 TEST_CONDITION = "defined(ENABLE_TESTS)" 470 471 def __init__(self, name, ver, flags=(), **kwargs): 472 """Positional arguments: 473 474 name -- The leaf name of the DLL. 475 476 ver -- The maximum version to be blocked. NB: The comparison used by the 477 blocklist is <=, so you should specify the last bad version, as opposed 478 to the first good version. 479 480 flags -- iterable containing the flags that should be applicable to 481 this blocklist entry. 482 483 Keyword arguments: 484 485 condition -- a string containing a C++ preprocessor expression. This 486 condition is written as a condition for an #if/#endif block that is 487 generated around the entry during output. 488 """ 489 490 self.check_ascii(name) 491 self._name = name.lower() 492 self._ver = Version(ver) 493 494 self._flags = set() 495 self.add_flags(flags) 496 if self._ver.is_timestamp(): 497 self._flags.add(USE_TIMESTAMP) 498 499 self._cond = kwargs.get("condition", set()) 500 if isinstance(self._cond, str): 501 self._cond = {self._cond} 502 503 @staticmethod 504 def check_ascii(name): 505 try: 506 # Supported in Python 3.7 507 if not name.isascii(): 508 raise ValueError('DLL name "%s" must be ASCII!' % name) 509 return 510 except AttributeError: 511 pass 512 513 try: 514 name.encode("ascii") 515 except UnicodeEncodeError: 516 raise ValueError('DLL name "%s" must be ASCII!' % name) 517 518 def get_name(self): 519 return self._name 520 521 def set_condition(self, cond): 522 self._cond.add(cond) 523 524 def get_condition(self): 525 if len(self._cond) == 1: 526 fmt = "{0}" 527 else: 528 fmt = "({0})" 529 530 return " && ".join([fmt.format(c) for c in self._cond]) 531 532 def set_is_test(self): 533 self.set_condition(DllBlocklistEntry.TEST_CONDITION) 534 535 def is_test(self): 536 return self._cond and DllBlocklistEntry.TEST_CONDITION in self._cond 537 538 def add_flags(self, new_flags): 539 if isinstance(new_flags, str): 540 self._flags.add(new_flags) 541 else: 542 self._flags.update(new_flags) 543 544 @staticmethod 545 def get_flag_string(flag): 546 return "DllBlockInfo::" + flag 547 548 def get_flags_list(self): 549 return self._flags 550 551 def write(self, output, mode): 552 if self._cond: 553 print("#if %s" % self.get_condition(), file=output) 554 555 flags_str = "" 556 557 flags = self.get_flags_list() 558 if flags: 559 flags_str = ", " + " | ".join(map(self.get_flag_string, flags)) 560 561 entry_str = ' DLL_BLOCKLIST_ENTRY("%s", %s%s)' % ( 562 self._name, 563 str(self._ver), 564 flags_str, 565 ) 566 print(entry_str, file=output) 567 568 if self._cond: 569 print("#endif // %s" % self.get_condition(), file=output) 570 571 572class A11yBlocklistEntry(DllBlocklistEntry): 573 """Represents a blocklist entry for injected a11y DLLs. This class does 574 not need to do anything special compared to a DllBlocklistEntry; we just 575 use this type to distinguish a11y entries from "regular" blocklist entries. 576 """ 577 578 def __init__(self, name, ver, flags=(), **kwargs): 579 """These arguments are identical to DllBlocklistEntry.__init__""" 580 581 super(A11yBlocklistEntry, self).__init__(name, ver, flags, **kwargs) 582 583 584class RedirectToNoOpEntryPoint(DllBlocklistEntry): 585 """Represents a blocklist entry to hook the entrypoint into a function 586 just returning TRUE to keep a module alive and harmless. 587 This entry is intended to block a DLL which is injected by IAT patching 588 which is planted by a kernel callback routine for LoadImage because 589 blocking such a DLL makes a process fail to launch. 590 """ 591 592 def __init__(self, name, ver, flags=(), **kwargs): 593 """These arguments are identical to DllBlocklistEntry.__init__""" 594 595 super(RedirectToNoOpEntryPoint, self).__init__(name, ver, flags, **kwargs) 596 597 def get_flags_list(self): 598 flags = super(RedirectToNoOpEntryPoint, self).get_flags_list() 599 # RedirectToNoOpEntryPoint items always include the following flag 600 flags.add(REDIRECT_TO_NOOP_ENTRYPOINT) 601 return flags 602 603 604class LspBlocklistEntry(DllBlocklistEntry): 605 """Represents a blocklist entry for a WinSock Layered Service Provider (LSP).""" 606 607 GUID_UNPACK_FMT_LE = "<IHHBBBBBBBB" 608 Guids = dict() 609 610 def __init__(self, name, ver, guids, flags=(), **kwargs): 611 """Positional arguments: 612 613 name -- The leaf name of the DLL. 614 615 ver -- The maximum version to be blocked. NB: The comparison used by the 616 blocklist is <=, so you should specify the last bad version, as opposed 617 to the first good version. 618 619 guids -- Either a string or list of strings containing the GUIDs that 620 uniquely identify the LSP. These GUIDs are generated by the developer of 621 the LSP and are registered with WinSock alongside the LSP. We record 622 this GUID as part of the "Winsock_LSP" annotation in our crash reports. 623 624 flags -- iterable containing the flags that should be applicable to 625 this blocklist entry. 626 627 Keyword arguments: 628 629 condition -- a string containing a C++ preprocessor expression. This 630 condition is written as a condition for an #if/#endif block that is 631 generated around the entry during output. 632 """ 633 634 super(LspBlocklistEntry, self).__init__(name, ver, flags, **kwargs) 635 if not guids: 636 raise ValueError("Missing GUID(s)!") 637 638 if isinstance(guids, str): 639 self.insert(UUID(guids), name) 640 else: 641 for guid in guids: 642 self.insert(UUID(guid), name) 643 644 def insert(self, guid, name): 645 # Some explanation here: Multiple DLLs (and thus multiple instances of 646 # LspBlocklistEntry) may share the same GUIDs. To ensure that we do not 647 # have any duplicates, we store each GUID in a class variable, Guids. 648 # We include the original DLL name from the blocklist definitions so 649 # that we may output a comment above each GUID indicating which entries 650 # correspond to which GUID. 651 LspBlocklistEntry.Guids.setdefault(guid, []).append(name) 652 653 def get_flags_list(self): 654 flags = super(LspBlocklistEntry, self).get_flags_list() 655 # LSP entries always include the following flag 656 flags.add(SUBSTITUTE_LSP_PASSTHROUGH) 657 return flags 658 659 @staticmethod 660 def as_c_struct(guid, names): 661 parts = unpack(LspBlocklistEntry.GUID_UNPACK_FMT_LE, guid.bytes_le) 662 str_guid = ( 663 " // %r\n // {%s}\n { 0x%08x, 0x%04x, 0x%04x, " 664 "{ 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x }" 665 " }" 666 % ( 667 names, 668 str(guid), 669 parts[0], 670 parts[1], 671 parts[2], 672 parts[3], 673 parts[4], 674 parts[5], 675 parts[6], 676 parts[7], 677 parts[8], 678 parts[9], 679 parts[10], 680 ) 681 ) 682 return str_guid 683 684 def write(self, output, mode): 685 if mode != LSP_MODE_GUID: 686 super(LspBlocklistEntry, self).write(output, mode) 687 return 688 689 # We dump the entire contents of Guids on the first call, and then 690 # clear it. Remaining invocations of this method are no-ops. 691 if LspBlocklistEntry.Guids: 692 result = ",\n".join( 693 [ 694 self.as_c_struct(guid, names) 695 for guid, names in iteritems(LspBlocklistEntry.Guids) 696 ] 697 ) 698 print(result, file=output) 699 LspBlocklistEntry.Guids.clear() 700 701 702def exec_script_file(script_name, globals): 703 with open(script_name) as script: 704 exec(compile(script.read(), script_name, "exec"), globals) 705 706 707def gen_blocklists(first_fd, defs_filename): 708 709 BlocklistDescriptor.set_output_fd(first_fd) 710 711 # exec_env defines the global variables that will be present in the 712 # execution environment when defs_filename is run by exec. 713 exec_env = { 714 # Add the blocklist entry types 715 "A11yBlocklistEntry": A11yBlocklistEntry, 716 "DllBlocklistEntry": DllBlocklistEntry, 717 "LspBlocklistEntry": LspBlocklistEntry, 718 "RedirectToNoOpEntryPoint": RedirectToNoOpEntryPoint, 719 # Add the special version types 720 "ALL_VERSIONS": Version.ALL_VERSIONS, 721 "UNVERSIONED": Version.UNVERSIONED, 722 "PETimeStamp": PETimeStamp, 723 } 724 725 # Import definition lists into exec_env 726 for defname in ALL_DEFINITION_LISTS: 727 exec_env[defname] = [] 728 # For each defname, add a special list for test-only entries 729 exec_env[derive_test_key(defname)] = [] 730 731 # Import flags into exec_env 732 exec_env.update({flag: flag for flag in INPUT_ONLY_FLAGS}) 733 734 # Now execute the input script with exec_env providing the globals 735 exec_script_file(defs_filename, exec_env) 736 737 # Tell the output descriptors to write out the output files. 738 for desc in GENERATED_BLOCKLIST_FILES: 739 desc.write(defs_filename, exec_env) 740