aboutsummaryrefslogtreecommitdiffstats
path: root/tools/syz-headerparser/headerlib/header_preprocessor.py
diff options
context:
space:
mode:
authorZubin Mithra <zsm@google.com>2017-08-23 14:01:57 +0100
committerDmitry Vyukov <dvyukov@google.com>2017-08-25 17:23:52 +0200
commitc3631fc789181c23aa51396f0ff66cd488e4b4f7 (patch)
tree7ffbc3082b6f8cac56de418bd51ee54d98eb3df7 /tools/syz-headerparser/headerlib/header_preprocessor.py
parent3f1aca4826c84d52da6047a8a5c9325727525c8d (diff)
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.
Diffstat (limited to 'tools/syz-headerparser/headerlib/header_preprocessor.py')
-rw-r--r--tools/syz-headerparser/headerlib/header_preprocessor.py142
1 files changed, 142 insertions, 0 deletions
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 <stdbool.h>
+#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 <stdint.h>
+%(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)
+ <class 'pycparser.c_ast.FileAST'>
+ '''
+
+ 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)