github.com/google/syzkaller@v0.0.0-20240517125934-c0f1611a36d6/tools/syz-headerparser/headerlib/header_preprocessor.py (about)

     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  '''
     5  This module provides classes which implement header file preprocessing.
     6  '''
     7  
     8  import logging
     9  import ntpath
    10  import os
    11  import subprocess
    12  import tempfile
    13  import traceback
    14  
    15  import pycparser
    16  
    17  template = '''
    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
    36  typedef 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  
    45  class HeaderFilePreprocessorException(Exception):
    46      '''Exceptions raised from HeaderFileParser. '''
    47      pass
    48  
    49  
    50  class 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)