From c3631fc789181c23aa51396f0ff66cd488e4b4f7 Mon Sep 17 00:00:00 2001 From: Zubin Mithra Date: Wed, 23 Aug 2017 14:01:57 +0100 Subject: tools: add headerparser as a tool to assist in writing system call descriptions The tool can be found inside tools/syz-headerparser. Details on how to use headerparser can be found inside docs/headerparser_usage.md. --- .../headerlib/header_preprocessor.py | 142 +++++++++++++++++++++ 1 file changed, 142 insertions(+) create mode 100644 tools/syz-headerparser/headerlib/header_preprocessor.py (limited to 'tools/syz-headerparser/headerlib/header_preprocessor.py') diff --git a/tools/syz-headerparser/headerlib/header_preprocessor.py b/tools/syz-headerparser/headerlib/header_preprocessor.py new file mode 100644 index 000000000..429d9688e --- /dev/null +++ b/tools/syz-headerparser/headerlib/header_preprocessor.py @@ -0,0 +1,142 @@ +# Copyright 2017 syzkaller project authors. All rights reserved. +# Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file. + +''' +This module provides classes which implement header file preprocessing. +''' + +import logging +import ntpath +import os +import subprocess +import tempfile +import traceback + +import pycparser + +template = ''' +#include +#define _GNU_SOURCE /* See feature_test_macros(7) */ + +// ------ MAKE PYCPARSER HAPPY ------ +#define __attribute__(...) +#define __inline inline +#define __restrict +#define __extension__ +// #define __sighandler_t int +#define __user + +#define __asm__(...) +#define __volatile__(...) +#define __signed__ signed +#define __int128_t unsigned long long // Hacky +#define __alignof__(...) 0 + +#define INIT // regex +typedef unsigned int size_t; +// ------ MAKE PYCPARSER HAPPY ------ + +#include +%(include_lines)s +%(header_file_includes)s +''' + + +class HeaderFilePreprocessorException(Exception): + '''Exceptions raised from HeaderFileParser. ''' + pass + + +class HeaderFilePreprocessor(object): + ''' + Given a C header filename, perform pre-processing and return an + ast that can be used for further processing. + + Usage : + + >>> import tempfile + >>> t = tempfile.NamedTemporaryFile() + >>> contents = """ + ... struct ARRAY_OF_POINTERS_CONTAINER { + ... unsigned int *ptr[10]; + ... int **n; + ... }; + ... + ... struct ARRAY_CONTAINER { + ... int g[10]; + ... int h[20][30]; + ... }; + ... + ... struct REGULAR_STRUCT { + ... int x; + ... char *y; + ... void *ptr; + ... }; + ... + ... struct STRUCT_WITH_STRUCT_PTR { + ... struct REGULAR_STRUCT *struct_ptr; + ... int z; + ... }; + ... """ + >>> t.write(contents) ; t.flush() + >>> h = HeaderFilePreprocessor([t.name]) + >>> ast = h.get_ast() + >>> print type(ast) + + ''' + + def __init__(self, filenames, include_lines='', loglvl=logging.INFO): + self.filenames = filenames + self.include_lines = include_lines + self._setuplogging(loglvl) + self._mktempfiles() + self._copyfiles() + self._gcc_preprocess() + + def execute(self, cmd): + self.logger.debug('HeaderFilePreprocessor.execute: %s', cmd) + p = subprocess.Popen(cmd, shell=True) + try: + os.waitpid(p.pid, 0) + except OSError as exception: + raise HeaderFilePreprocessorException(exception) + + def _setuplogging(self, loglvl): + self.logger = logging.getLogger(self.__class__.__name__) + formatter = logging.Formatter('DEBUG:%(name)s:%(message)s') + sh = logging.StreamHandler() + sh.setFormatter(formatter) + sh.setLevel(loglvl) + self.logger.addHandler(sh) + self.logger.setLevel(loglvl) + + def _copyfiles(self): + self.execute('cp %s %s' % (' '.join(self.filenames), self.tempdir)) + + def _mktempfiles(self): + self.tempdir = tempfile.mkdtemp() + self.temp_sourcefile = os.path.join(self.tempdir, 'source.c') + self.temp_objectfile = os.path.join(self.tempdir, 'source.o') + self.logger.debug(('HeaderFilePreprocessor._mktempfiles: sourcefile=%s' + 'objectfile=%s'), self.temp_sourcefile, self.temp_objectfile) + + header_file_includes = '' + include_lines = self.include_lines + for name in self.filenames: + header_file_includes = '%s#include "%s"\n' % (header_file_includes, + ntpath.basename(name)) + + open(self.temp_sourcefile, 'w').write(template % (locals())) + + def _gcc_preprocess(self): + self.execute('gcc -I. -E -P -c %s > %s' + % (self.temp_sourcefile, self.temp_objectfile)) + + def _get_ast(self): + return pycparser.parse_file(self.temp_objectfile) + + def get_ast(self): + try: + return self._get_ast() + except pycparser.plyparser.ParseError as e: + raise HeaderFilePreprocessorException(e) -- cgit mrf-deployment