• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# Copyright 2017 syzkaller project authors. All rights reserved.
2# Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file.
3
4'''
5This module provides classes which implement header file preprocessing.
6'''
7
8import logging
9import ntpath
10import os
11import subprocess
12import tempfile
13import traceback
14
15import pycparser
16
17template = '''
18#include <stdbool.h>
19#define _GNU_SOURCE             /* See feature_test_macros(7) */
20
21// ------ MAKE PYCPARSER HAPPY ------
22#define __attribute__(...)
23#define __inline inline
24#define __restrict
25#define __extension__
26// #define __sighandler_t int
27#define __user
28
29#define __asm__(...)
30#define __volatile__(...)
31#define __signed__ signed
32#define __int128_t unsigned long long // Hacky
33#define __alignof__(...) 0
34
35#define INIT // regex
36typedef unsigned int size_t;
37// ------ MAKE PYCPARSER HAPPY ------
38
39#include <stdint.h>
40%(include_lines)s
41%(header_file_includes)s
42'''
43
44
45class HeaderFilePreprocessorException(Exception):
46    '''Exceptions raised from HeaderFileParser. '''
47    pass
48
49
50class HeaderFilePreprocessor(object):
51    '''
52    Given a C header filename, perform pre-processing and return an
53    ast that can be used for further processing.
54
55    Usage :
56
57    >>> import tempfile
58    >>> t = tempfile.NamedTemporaryFile()
59    >>> contents = """
60    ... struct ARRAY_OF_POINTERS_CONTAINER {
61    ... unsigned int *ptr[10];
62    ... int **n;
63    ... };
64    ...
65    ... struct ARRAY_CONTAINER {
66    ... int g[10];
67    ... int h[20][30];
68    ... };
69    ...
70    ... struct REGULAR_STRUCT {
71    ... int x;
72    ... char *y;
73    ... void *ptr;
74    ... };
75    ...
76    ... struct STRUCT_WITH_STRUCT_PTR {
77    ... struct REGULAR_STRUCT *struct_ptr;
78    ... int z;
79    ... };
80    ... """
81    >>> t.write(contents) ; t.flush()
82    >>> h = HeaderFilePreprocessor([t.name])
83    >>> ast = h.get_ast()
84    >>> print type(ast)
85    <class 'pycparser.c_ast.FileAST'>
86    '''
87
88    def __init__(self, filenames, include_lines='', loglvl=logging.INFO):
89        self.filenames = filenames
90        self.include_lines = include_lines
91        self._setuplogging(loglvl)
92        self._mktempfiles()
93        self._copyfiles()
94        self._gcc_preprocess()
95
96    def execute(self, cmd):
97        self.logger.debug('HeaderFilePreprocessor.execute: %s', cmd)
98        p = subprocess.Popen(cmd, shell=True)
99        try:
100            os.waitpid(p.pid, 0)
101        except OSError as exception:
102            raise HeaderFilePreprocessorException(exception)
103
104    def _setuplogging(self, loglvl):
105        self.logger = logging.getLogger(self.__class__.__name__)
106        formatter = logging.Formatter('DEBUG:%(name)s:%(message)s')
107        sh = logging.StreamHandler()
108        sh.setFormatter(formatter)
109        sh.setLevel(loglvl)
110        self.logger.addHandler(sh)
111        self.logger.setLevel(loglvl)
112
113    def _copyfiles(self):
114        self.execute('cp %s %s' % (' '.join(self.filenames), self.tempdir))
115
116    def _mktempfiles(self):
117        self.tempdir = tempfile.mkdtemp()
118        self.temp_sourcefile = os.path.join(self.tempdir, 'source.c')
119        self.temp_objectfile = os.path.join(self.tempdir, 'source.o')
120        self.logger.debug(('HeaderFilePreprocessor._mktempfiles: sourcefile=%s'
121                           'objectfile=%s'), self.temp_sourcefile, self.temp_objectfile)
122
123        header_file_includes = ''
124        include_lines = self.include_lines
125        for name in self.filenames:
126            header_file_includes = '%s#include "%s"\n' % (header_file_includes,
127                                                          ntpath.basename(name))
128
129        open(self.temp_sourcefile, 'w').write(template % (locals()))
130
131    def _gcc_preprocess(self):
132        self.execute('gcc -I. -E -P -c %s > %s'
133                                        % (self.temp_sourcefile, self.temp_objectfile))
134
135    def _get_ast(self):
136        return pycparser.parse_file(self.temp_objectfile)
137
138    def get_ast(self):
139        try:
140            return self._get_ast()
141        except pycparser.plyparser.ParseError as e:
142            raise HeaderFilePreprocessorException(e)
143