blob: 6c9d670f75ba052992a7d8fa9774c7ef4ed0d195 [file] [log] [blame]
Jerry Yue78ee992021-09-22 15:42:14 +08001#!/usr/bin/env python3
2
3"""Generate library/ssl_debug_helps_generated.c
4
5The code generated by this module includes debug helper functions that can not be
6implemented by fixed codes.
7
8"""
9
10# Copyright The Mbed TLS Contributors
11# SPDX-License-Identifier: Apache-2.0
12#
13# Licensed under the Apache License, Version 2.0 (the "License"); you may
14# not use this file except in compliance with the License.
15# You may obtain a copy of the License at
16#
17# http://www.apache.org/licenses/LICENSE-2.0
18#
19# Unless required by applicable law or agreed to in writing, software
20# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
21# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
22# See the License for the specific language governing permissions and
23# limitations under the License.
24import sys
25import re
26import os
27import textwrap
Jerry Yue6369b02021-12-02 13:51:26 +080028import argparse
Jerry Yue78ee992021-09-22 15:42:14 +080029from mbedtls_dev import build_tree
30
Jerry Yue6369b02021-12-02 13:51:26 +080031
Jerry Yue78ee992021-09-22 15:42:14 +080032def remove_c_comments(string):
33 """
34 Remove C style comments from input string
35 """
36 string_pattern = r"(?P<string>\".*?\"|\'.*?\')"
37 comment_pattern = r"(?P<comment>/\*.*?\*/|//[^\r\n]*$)"
38 pattern = re.compile(string_pattern + r'|' + comment_pattern,
Jerry Yue6369b02021-12-02 13:51:26 +080039 re.MULTILINE | re.DOTALL)
40
Jerry Yue78ee992021-09-22 15:42:14 +080041 def replacer(match):
42 if match.lastgroup == 'comment':
43 return ""
44 return match.group()
45 return pattern.sub(replacer, string)
46
Jerry Yue6369b02021-12-02 13:51:26 +080047
Jerry Yue78ee992021-09-22 15:42:14 +080048class CondDirectiveNotMatch(Exception):
49 pass
50
Jerry Yue6369b02021-12-02 13:51:26 +080051
Jerry Yue988f0f2021-11-11 13:22:20 +080052def preprocess_c_source_code(source, *classes):
Jerry Yue78ee992021-09-22 15:42:14 +080053 """
54 Simple preprocessor for C source code.
55
Jerry Yu6389b252021-12-02 10:28:40 +080056 Only processses condition directives without expanding them.
57 Yield object according to the classes input. Most match firstly
Jerry Yue78ee992021-09-22 15:42:14 +080058
Jerry Yu6389b252021-12-02 10:28:40 +080059 If the directive pair does not match , raise CondDirectiveNotMatch.
Jerry Yue78ee992021-09-22 15:42:14 +080060
61 Assume source code does not include comments and compile pass.
62
63 """
64
65 pattern = re.compile(r"^[ \t]*#[ \t]*" +
66 r"(?P<directive>(if[ \t]|ifndef[ \t]|ifdef[ \t]|else|endif))" +
67 r"[ \t]*(?P<param>(.*\\\n)*.*$)",
68 re.MULTILINE)
69 stack = []
70
71 def _yield_objects(s, d, p, st, end):
72 """
73 Output matched source piece
74 """
75 nonlocal stack
76 start_line, end_line = '', ''
77 if stack:
78 start_line = '#{} {}'.format(d, p)
79 if d == 'if':
80 end_line = '#endif /* {} */'.format(p)
81 elif d == 'ifdef':
82 end_line = '#endif /* defined({}) */'.format(p)
83 else:
84 end_line = '#endif /* !defined({}) */'.format(p)
85 has_instance = False
86 for cls in classes:
87 for instance in cls.extract(s, st, end):
88 if has_instance is False:
89 has_instance = True
90 yield pair_start, start_line
Jerry Yubfcfe742022-02-22 16:41:39 +080091 if instance:
92 yield instance.span()[0], instance
Jerry Yue78ee992021-09-22 15:42:14 +080093 if has_instance:
94 yield start, end_line
95
96 for match in pattern.finditer(source):
97
98 directive = match.groupdict()['directive'].strip()
99 param = match.groupdict()['param']
100 start, end = match.span()
101
102 if directive in ('if', 'ifndef', 'ifdef'):
103 stack.append((directive, param, start, end))
104 continue
105
106 if not stack:
107 raise CondDirectiveNotMatch()
108
109 pair_directive, pair_param, pair_start, pair_end = stack.pop()
110 yield from _yield_objects(source,
111 pair_directive,
112 pair_param,
113 pair_end,
114 start)
115
116 if directive == 'endif':
117 continue
118
119 if pair_directive == 'if':
120 directive = 'if'
121 param = "!( {} )".format(pair_param)
122 elif pair_directive == 'ifdef':
123 directive = 'ifndef'
124 param = pair_param
125 else:
126 directive = 'ifdef'
127 param = pair_param
128
129 stack.append((directive, param, start, end))
130 assert not stack, len(stack)
131
132
Jerry Yue78ee992021-09-22 15:42:14 +0800133class EnumDefinition:
134 """
135 Generate helper functions around enumeration.
136
Jerry Yu6389b252021-12-02 10:28:40 +0800137 Currently, it generate translation function from enum value to string.
Jerry Yue78ee992021-09-22 15:42:14 +0800138 Enum definition looks like:
139 [typedef] enum [prefix name] { [body] } [suffix name];
140
141 Known limitation:
142 - the '}' and ';' SHOULD NOT exist in different macro blocks. Like
143 ```
144 enum test {
145 ....
146 #if defined(A)
147 ....
148 };
149 #else
150 ....
151 };
152 #endif
153 ```
154 """
155
156 @classmethod
157 def extract(cls, source_code, start=0, end=-1):
158 enum_pattern = re.compile(r'enum\s*(?P<prefix_name>\w*)\s*' +
159 r'{\s*(?P<body>[^}]*)}' +
160 r'\s*(?P<suffix_name>\w*)\s*;',
Jerry Yue6369b02021-12-02 13:51:26 +0800161 re.MULTILINE | re.DOTALL)
Jerry Yue78ee992021-09-22 15:42:14 +0800162
163 for match in enum_pattern.finditer(source_code, start, end):
164 yield EnumDefinition(source_code,
165 span=match.span(),
166 group=match.groupdict())
167
168 def __init__(self, source_code, span=None, group=None):
169 assert isinstance(group, dict)
170 prefix_name = group.get('prefix_name', None)
171 suffix_name = group.get('suffix_name', None)
172 body = group.get('body', None)
173 assert prefix_name or suffix_name
174 assert body
175 assert span
176 # If suffix_name exists, it is a typedef
177 self._prototype = suffix_name if suffix_name else 'enum ' + prefix_name
178 self._name = suffix_name if suffix_name else prefix_name
179 self._body = body
180 self._source = source_code
181 self._span = span
182
183 def __repr__(self):
184 return 'Enum({},{})'.format(self._name, self._span)
185
186 def __str__(self):
187 return repr(self)
188
189 def span(self):
190 return self._span
191
David Horstmann3be12712021-12-16 10:56:26 +0000192 def generate_translation_function(self):
Jerry Yue78ee992021-09-22 15:42:14 +0800193 """
194 Generate function for translating value to string
195 """
196 translation_table = []
197
198 for line in self._body.splitlines():
199
200 if line.strip().startswith('#'):
201 # Preprocess directive, keep it in table
202 translation_table.append(line.strip())
203 continue
204
205 if not line.strip():
206 continue
207
208 for field in line.strip().split(','):
209 if not field.strip():
210 continue
211 member = field.strip().split()[0]
212 translation_table.append(
213 '{space}[{member}] = "{member}",'.format(member=member,
214 space=' '*8)
215 )
216
217 body = textwrap.dedent('''\
218 const char *{name}_str( {prototype} in )
219 {{
220 const char * in_to_str[]=
221 {{
222 {translation_table}
223 }};
224
225 if( in > ( sizeof( in_to_str )/sizeof( in_to_str[0]) - 1 ) ||
226 in_to_str[ in ] == NULL )
227 {{
228 return "UNKOWN_VAULE";
229 }}
230 return in_to_str[ in ];
231 }}
232 ''')
233 body = body.format(translation_table='\n'.join(translation_table),
234 name=self._name,
235 prototype=self._prototype)
Gilles Peskineccbc3182021-12-15 12:55:37 +0100236 return body
Jerry Yue78ee992021-09-22 15:42:14 +0800237
Jerry Yubfcfe742022-02-22 16:41:39 +0800238class SignatureAlgorithmDefinition:
239 """
240 Generate helper functions for signature algorithms.
241
242 It generates translation function from signature algorithm define to string.
243 Signature algorithm definition looks like:
244 #define MBEDTLS_TLS1_3_SIG_[ upper case signature algorithm ] [ value(hex) ]
245
246 Known limitation:
247 - the definitions SHOULD exist in same macro blocks.
248 """
249
250 @classmethod
251 def extract(cls, source_code, start=0, end=-1):
252 sig_alg_pattern = re.compile(r'#define\s+(?P<name>MBEDTLS_TLS1_3_SIG_\w+)\s+' +
253 r'(?P<value>0[xX][0-9a-fA-F]+)$',
254 re.MULTILINE | re.DOTALL)
255 matches = list(sig_alg_pattern.finditer(source_code, start, end))
256 if matches:
257 yield SignatureAlgorithmDefinition(source_code, definitions=matches)
258
259 def __init__(self, source_code, definitions=None):
260 if definitions is None:
261 definitions = []
262 assert isinstance(definitions, list) and definitions
263 self._definitions = definitions
264 self._source = source_code
265
266 def __repr__(self):
267 return 'SigAlgs({})'.format(self._definitions[0].span())
268
269 def span(self):
270 return self._definitions[0].span()
271 def __str__(self):
272 """
273 Generate function for translating value to string
274 """
275 translation_table = []
276 for m in self._definitions:
277 name = m.groupdict()['name']
278 translation_table.append(
279 '\tcase {}:\n\t return "{}";'.format(name,
280 name[len('MBEDTLS_TLS1_3_SIG_'):].lower())
281 )
282
283 body = textwrap.dedent('''\
284 const char *mbedtls_ssl_sig_alg_to_str( uint16_t in )
285 {{
286 switch( in )
287 {{
288 {translation_table}
289 }};
290
291 return "UNKOWN";
292 }}''')
293 body = body.format(translation_table='\n'.join(translation_table))
294 return body
Jerry Yue6369b02021-12-02 13:51:26 +0800295
Jerry Yue78ee992021-09-22 15:42:14 +0800296OUTPUT_C_TEMPLATE = '''\
297/* Automatically generated by generate_ssl_debug_helpers.py. DO NOT EDIT. */
298
Gilles Peskine863b96a2021-12-16 10:04:58 +0100299/**
300 * \file ssl_debug_helpers_generated.c
301 *
302 * \brief Automatically generated helper functions for debugging
303 */
304/*
305 * Copyright The Mbed TLS Contributors
306 * SPDX-License-Identifier: Apache-2.0
307 *
308 * Licensed under the Apache License, Version 2.0 (the "License"); you may
309 * not use this file except in compliance with the License.
310 * You may obtain a copy of the License at
311 *
312 * http://www.apache.org/licenses/LICENSE-2.0
313 *
314 * Unless required by applicable law or agreed to in writing, software
315 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
316 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
317 * See the License for the specific language governing permissions and
318 * limitations under the License.
319 */
320
Jerry Yue78ee992021-09-22 15:42:14 +0800321#include "common.h"
322
323#if defined(MBEDTLS_DEBUG_C)
324
Gilles Peskine923d5c92021-12-15 12:56:54 +0100325#include "ssl_debug_helpers.h"
Jerry Yue78ee992021-09-22 15:42:14 +0800326
327{functions}
328
329#endif /* MBEDTLS_DEBUG_C */
330/* End of automatically generated file. */
331
332'''
333
Jerry Yue78ee992021-09-22 15:42:14 +0800334
Jerry Yue6369b02021-12-02 13:51:26 +0800335def generate_ssl_debug_helpers(output_directory, mbedtls_root):
Jerry Yue78ee992021-09-22 15:42:14 +0800336 """
337 Generate functions of debug helps
338 """
Jerry Yu0cb2cf62021-12-10 14:21:27 +0800339 mbedtls_root = os.path.abspath(mbedtls_root or build_tree.guess_mbedtls_root())
Jerry Yue6369b02021-12-02 13:51:26 +0800340 with open(os.path.join(mbedtls_root, 'include/mbedtls/ssl.h')) as f:
Jerry Yue78ee992021-09-22 15:42:14 +0800341 source_code = remove_c_comments(f.read())
342
343 definitions = dict()
Jerry Yubfcfe742022-02-22 16:41:39 +0800344 for start, instance in preprocess_c_source_code(source_code,
345 EnumDefinition,
346 SignatureAlgorithmDefinition):
Jerry Yue78ee992021-09-22 15:42:14 +0800347 if start in definitions:
348 continue
349 if isinstance(instance, EnumDefinition):
David Horstmann3be12712021-12-16 10:56:26 +0000350 definition = instance.generate_translation_function()
Jerry Yue78ee992021-09-22 15:42:14 +0800351 else:
352 definition = instance
Jerry Yue78ee992021-09-22 15:42:14 +0800353 definitions[start] = definition
Jerry Yue78ee992021-09-22 15:42:14 +0800354
Jerry Yue6369b02021-12-02 13:51:26 +0800355 function_definitions = [str(v) for _, v in sorted(definitions.items())]
Jerry Yue6369b02021-12-02 13:51:26 +0800356 if output_directory == sys.stdout:
Jerry Yue6369b02021-12-02 13:51:26 +0800357 sys.stdout.write(OUTPUT_C_TEMPLATE.format(
358 functions='\n'.join(function_definitions)))
359 else:
360 with open(os.path.join(output_directory, 'ssl_debug_helpers_generated.c'), 'w') as f:
361 f.write(OUTPUT_C_TEMPLATE.format(
362 functions='\n'.join(function_definitions)))
Jerry Yue78ee992021-09-22 15:42:14 +0800363
Jerry Yue78ee992021-09-22 15:42:14 +0800364
Jerry Yue6369b02021-12-02 13:51:26 +0800365def main():
366 """
367 Command line entry
368 """
369 parser = argparse.ArgumentParser()
Jerry Yu0cb2cf62021-12-10 14:21:27 +0800370 parser.add_argument('--mbedtls-root', nargs='?', default=None,
Jerry Yue6369b02021-12-02 13:51:26 +0800371 help='root directory of mbedtls source code')
372 parser.add_argument('output_directory', nargs='?',
Jerry Yu9817e3e2021-12-02 14:32:58 +0800373 default='library', help='source/header files location')
Jerry Yue78ee992021-09-22 15:42:14 +0800374
Jerry Yue6369b02021-12-02 13:51:26 +0800375 args = parser.parse_args()
376
377 generate_ssl_debug_helpers(args.output_directory, args.mbedtls_root)
378 return 0
Jerry Yue78ee992021-09-22 15:42:14 +0800379
380
381if __name__ == '__main__':
Jerry Yue6369b02021-12-02 13:51:26 +0800382 sys.exit(main())