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