1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149
|
# Copyright 2023 The Chromium Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""
Generates the list of valid imports for the lowest-supported version of
Windows.
Run from the root directory of the checkout - builds a .inc file that
will be included into delayloads_unittest.cc.
To export Chrome's view of the data for other build systems, run with the --json
flag and specify the --apisets-file.
"""
import argparse
import json
import os
import re
import sys
USE_PYTHON_3 = f'This script will only run under python3.'
# e.g. ' Section contains the following exports for CRYPT32.dll'
RE_NEWMOD = re.compile(
r'Section contains the following exports for (?P<dll>\w+\.(?i:dll|drv))')
# e.g. ' 1020 0 00088A30 CertAddCRLContextToStore'
# ^ can be blank
RE_EXPORT = re.compile(r'^\s+\d+\s+[0-9A-F]+\s+[0-9A-F ]{8}\s+(?P<export>\w+)')
# apiset line in apisets.inc (see generate_supported_apisets.py)
RE_APISET = re.compile(r'^{"(?P<apiset>[^"]+)",\s*(?P<version>\d+)},')
def parse_file(f):
"""Naive parser for dumpbin output.
f: filehandle to file containing dumpbin output."""
mods = dict()
curmod = None
imports = []
for line in f.readlines():
# e.g. ' Section contains the following exports for CRYPT32.dll'
m = re.search(RE_NEWMOD, line)
if m:
if curmod:
mods[curmod] = imports
imports = []
curmod = m.group('dll').lower()
continue
if curmod is None:
continue
# e.g. ' 1020 0 00088A30 CertAddCRLContextToStore'
m = re.search(RE_EXPORT, line)
if m:
imports.append(m.group('export'))
if curmod:
mods[curmod] = imports
return mods
def generate_inc(input_file):
"""Reads output of dumpbin /exports *.dll and makes input for .inc C++ file.
input_file: path to file containing output of `dumpbin /exports *.dll`.
using DetailedImports = std::map<std::string, std::set<std::string>>;
"""
# const DetailedImports kVariable = {
mods = parse_file(open(input_file, 'r', encoding='utf-8'))
module_entries = [];
for module, functions in mods.items():
joined_functions = ',\n'.join([f' "{fn}"' for fn in functions])
module_line = f' {{"{module}", {{{joined_functions}}}}}'
module_entries.append(module_line)
all_modules = (',\n').join(module_entries)
return all_modules
# };
def maybe_read(filename):
""" Read existing file so that we don't write it again if it hasn't changed"""
if not os.path.isfile(filename):
return None;
try:
with open(filename, 'r', encoding='utf-8') as f:
return f.read();
except Exception:
return None
def write_imports_inc(input, output):
existing_content = maybe_read(output)
new_content = generate_inc(input)
if existing_content == new_content:
return
os.makedirs(os.path.dirname(output), exist_ok=True)
with open(output, 'w', encoding='utf-8', newline='') as f:
f.write(new_content)
def parse_apisets(f):
# Parses output of generate_supported_apisets.py
apisets = dict()
for line in f.readlines():
m = re.search(RE_APISET, line)
if m:
apisets[m.group("apiset").lower()] = m.group("version")
return apisets
def write_json(exports_file, apisets_file, output):
# This generates a pbtext used in google3 for a similar check.
exports = parse_file(open(exports_file, 'r', encoding='utf-8'))
apisets = parse_apisets(open(apisets_file, 'r', encoding='utf-8'))
result = {
"exports": exports,
"apisets": apisets,
}
with open(output, 'w', encoding='utf-8', newline='') as f:
json.dump(result, f, indent=2)
def main():
parser = argparse.ArgumentParser(
description=__doc__, formatter_class=argparse.RawTextHelpFormatter)
parser.add_argument('--exports-file',
default="chrome/test/delayload/supported_imports.txt",
metavar='FILE_NAME',
help='output of dumpbin /exports *.dll')
parser.add_argument('--apisets-file',
default="chrome/test/delayload/apisets.inc",
metavar='FILE_NAME',
help='[optional] output of generate_supported_apisets.py')
parser.add_argument('--out-file',
default='gen/chrome/test/delayload/supported_imports.inc',
metavar='FILE_NAME',
help='path to write .inc or .json file, within out-dir')
parser.add_argument('--json', action='store_true',
help='output json instead of .inc')
args, _extras = parser.parse_known_args()
if args.json:
# Used to export Chrome's data for other build systems.
write_json(args.exports_file, args.apisets_file, args.out_file)
else:
# Used in Chrome build.
write_imports_inc(args.exports_file, args.out_file)
if __name__ == '__main__':
sys.exit(main())
|