forked from OSchip/llvm-project
				
			
		
			
				
	
	
		
			260 lines
		
	
	
		
			8.3 KiB
		
	
	
	
		
			Python
		
	
	
	
			
		
		
	
	
			260 lines
		
	
	
		
			8.3 KiB
		
	
	
	
		
			Python
		
	
	
	
| # -*- coding: utf-8 -*-
 | |
| # Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
 | |
| # See https://llvm.org/LICENSE.txt for license information.
 | |
| # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
 | |
| """ This module compiles the intercept library. """
 | |
| 
 | |
| import sys
 | |
| import os
 | |
| import os.path
 | |
| import re
 | |
| import tempfile
 | |
| import shutil
 | |
| import contextlib
 | |
| import logging
 | |
| 
 | |
| __all__ = ['build_libear']
 | |
| 
 | |
| 
 | |
| def build_libear(compiler, dst_dir):
 | |
|     """ Returns the full path to the 'libear' library. """
 | |
| 
 | |
|     try:
 | |
|         src_dir = os.path.dirname(os.path.realpath(__file__))
 | |
|         toolset = make_toolset(src_dir)
 | |
|         toolset.set_compiler(compiler)
 | |
|         toolset.set_language_standard('c99')
 | |
|         toolset.add_definitions(['-D_GNU_SOURCE'])
 | |
| 
 | |
|         configure = do_configure(toolset)
 | |
|         configure.check_function_exists('execve', 'HAVE_EXECVE')
 | |
|         configure.check_function_exists('execv', 'HAVE_EXECV')
 | |
|         configure.check_function_exists('execvpe', 'HAVE_EXECVPE')
 | |
|         configure.check_function_exists('execvp', 'HAVE_EXECVP')
 | |
|         configure.check_function_exists('execvP', 'HAVE_EXECVP2')
 | |
|         configure.check_function_exists('exect', 'HAVE_EXECT')
 | |
|         configure.check_function_exists('execl', 'HAVE_EXECL')
 | |
|         configure.check_function_exists('execlp', 'HAVE_EXECLP')
 | |
|         configure.check_function_exists('execle', 'HAVE_EXECLE')
 | |
|         configure.check_function_exists('posix_spawn', 'HAVE_POSIX_SPAWN')
 | |
|         configure.check_function_exists('posix_spawnp', 'HAVE_POSIX_SPAWNP')
 | |
|         configure.check_symbol_exists('_NSGetEnviron', 'crt_externs.h',
 | |
|                                       'HAVE_NSGETENVIRON')
 | |
|         configure.write_by_template(
 | |
|             os.path.join(src_dir, 'config.h.in'),
 | |
|             os.path.join(dst_dir, 'config.h'))
 | |
| 
 | |
|         target = create_shared_library('ear', toolset)
 | |
|         target.add_include(dst_dir)
 | |
|         target.add_sources('ear.c')
 | |
|         target.link_against(toolset.dl_libraries())
 | |
|         target.link_against(['pthread'])
 | |
|         target.build_release(dst_dir)
 | |
| 
 | |
|         return os.path.join(dst_dir, target.name)
 | |
| 
 | |
|     except Exception:
 | |
|         logging.info("Could not build interception library.", exc_info=True)
 | |
|         return None
 | |
| 
 | |
| 
 | |
| def execute(cmd, *args, **kwargs):
 | |
|     """ Make subprocess execution silent. """
 | |
| 
 | |
|     import subprocess
 | |
|     kwargs.update({'stdout': subprocess.PIPE, 'stderr': subprocess.STDOUT})
 | |
|     return subprocess.check_call(cmd, *args, **kwargs)
 | |
| 
 | |
| 
 | |
| @contextlib.contextmanager
 | |
| def TemporaryDirectory(**kwargs):
 | |
|     name = tempfile.mkdtemp(**kwargs)
 | |
|     try:
 | |
|         yield name
 | |
|     finally:
 | |
|         shutil.rmtree(name)
 | |
| 
 | |
| 
 | |
| class Toolset(object):
 | |
|     """ Abstract class to represent different toolset. """
 | |
| 
 | |
|     def __init__(self, src_dir):
 | |
|         self.src_dir = src_dir
 | |
|         self.compiler = None
 | |
|         self.c_flags = []
 | |
| 
 | |
|     def set_compiler(self, compiler):
 | |
|         """ part of public interface """
 | |
|         self.compiler = compiler
 | |
| 
 | |
|     def set_language_standard(self, standard):
 | |
|         """ part of public interface """
 | |
|         self.c_flags.append('-std=' + standard)
 | |
| 
 | |
|     def add_definitions(self, defines):
 | |
|         """ part of public interface """
 | |
|         self.c_flags.extend(defines)
 | |
| 
 | |
|     def dl_libraries(self):
 | |
|         raise NotImplementedError()
 | |
| 
 | |
|     def shared_library_name(self, name):
 | |
|         raise NotImplementedError()
 | |
| 
 | |
|     def shared_library_c_flags(self, release):
 | |
|         extra = ['-DNDEBUG', '-O3'] if release else []
 | |
|         return extra + ['-fPIC'] + self.c_flags
 | |
| 
 | |
|     def shared_library_ld_flags(self, release, name):
 | |
|         raise NotImplementedError()
 | |
| 
 | |
| 
 | |
| class DarwinToolset(Toolset):
 | |
|     def __init__(self, src_dir):
 | |
|         Toolset.__init__(self, src_dir)
 | |
| 
 | |
|     def dl_libraries(self):
 | |
|         return []
 | |
| 
 | |
|     def shared_library_name(self, name):
 | |
|         return 'lib' + name + '.dylib'
 | |
| 
 | |
|     def shared_library_ld_flags(self, release, name):
 | |
|         extra = ['-dead_strip'] if release else []
 | |
|         return extra + ['-dynamiclib', '-install_name', '@rpath/' + name]
 | |
| 
 | |
| 
 | |
| class UnixToolset(Toolset):
 | |
|     def __init__(self, src_dir):
 | |
|         Toolset.__init__(self, src_dir)
 | |
| 
 | |
|     def dl_libraries(self):
 | |
|         return []
 | |
| 
 | |
|     def shared_library_name(self, name):
 | |
|         return 'lib' + name + '.so'
 | |
| 
 | |
|     def shared_library_ld_flags(self, release, name):
 | |
|         extra = [] if release else []
 | |
|         return extra + ['-shared', '-Wl,-soname,' + name]
 | |
| 
 | |
| 
 | |
| class LinuxToolset(UnixToolset):
 | |
|     def __init__(self, src_dir):
 | |
|         UnixToolset.__init__(self, src_dir)
 | |
| 
 | |
|     def dl_libraries(self):
 | |
|         return ['dl']
 | |
| 
 | |
| 
 | |
| def make_toolset(src_dir):
 | |
|     platform = sys.platform
 | |
|     if platform in {'win32', 'cygwin'}:
 | |
|         raise RuntimeError('not implemented on this platform')
 | |
|     elif platform == 'darwin':
 | |
|         return DarwinToolset(src_dir)
 | |
|     elif platform in {'linux', 'linux2'}:
 | |
|         return LinuxToolset(src_dir)
 | |
|     else:
 | |
|         return UnixToolset(src_dir)
 | |
| 
 | |
| 
 | |
| class Configure(object):
 | |
|     def __init__(self, toolset):
 | |
|         self.ctx = toolset
 | |
|         self.results = {'APPLE': sys.platform == 'darwin'}
 | |
| 
 | |
|     def _try_to_compile_and_link(self, source):
 | |
|         try:
 | |
|             with TemporaryDirectory() as work_dir:
 | |
|                 src_file = 'check.c'
 | |
|                 with open(os.path.join(work_dir, src_file), 'w') as handle:
 | |
|                     handle.write(source)
 | |
| 
 | |
|                 execute([self.ctx.compiler, src_file] + self.ctx.c_flags,
 | |
|                         cwd=work_dir)
 | |
|                 return True
 | |
|         except Exception:
 | |
|             return False
 | |
| 
 | |
|     def check_function_exists(self, function, name):
 | |
|         template = "int FUNCTION(); int main() { return FUNCTION(); }"
 | |
|         source = template.replace("FUNCTION", function)
 | |
| 
 | |
|         logging.debug('Checking function %s', function)
 | |
|         found = self._try_to_compile_and_link(source)
 | |
|         logging.debug('Checking function %s -- %s', function,
 | |
|                       'found' if found else 'not found')
 | |
|         self.results.update({name: found})
 | |
| 
 | |
|     def check_symbol_exists(self, symbol, include, name):
 | |
|         template = """#include <INCLUDE>
 | |
|                       int main() { return ((int*)(&SYMBOL))[0]; }"""
 | |
|         source = template.replace('INCLUDE', include).replace("SYMBOL", symbol)
 | |
| 
 | |
|         logging.debug('Checking symbol %s', symbol)
 | |
|         found = self._try_to_compile_and_link(source)
 | |
|         logging.debug('Checking symbol %s -- %s', symbol,
 | |
|                       'found' if found else 'not found')
 | |
|         self.results.update({name: found})
 | |
| 
 | |
|     def write_by_template(self, template, output):
 | |
|         def transform(line, definitions):
 | |
| 
 | |
|             pattern = re.compile(r'^#cmakedefine\s+(\S+)')
 | |
|             m = pattern.match(line)
 | |
|             if m:
 | |
|                 key = m.group(1)
 | |
|                 if key not in definitions or not definitions[key]:
 | |
|                     return '/* #undef {0} */{1}'.format(key, os.linesep)
 | |
|                 else:
 | |
|                     return '#define {0}{1}'.format(key, os.linesep)
 | |
|             return line
 | |
| 
 | |
|         with open(template, 'r') as src_handle:
 | |
|             logging.debug('Writing config to %s', output)
 | |
|             with open(output, 'w') as dst_handle:
 | |
|                 for line in src_handle:
 | |
|                     dst_handle.write(transform(line, self.results))
 | |
| 
 | |
| 
 | |
| def do_configure(toolset):
 | |
|     return Configure(toolset)
 | |
| 
 | |
| 
 | |
| class SharedLibrary(object):
 | |
|     def __init__(self, name, toolset):
 | |
|         self.name = toolset.shared_library_name(name)
 | |
|         self.ctx = toolset
 | |
|         self.inc = []
 | |
|         self.src = []
 | |
|         self.lib = []
 | |
| 
 | |
|     def add_include(self, directory):
 | |
|         self.inc.extend(['-I', directory])
 | |
| 
 | |
|     def add_sources(self, source):
 | |
|         self.src.append(source)
 | |
| 
 | |
|     def link_against(self, libraries):
 | |
|         self.lib.extend(['-l' + lib for lib in libraries])
 | |
| 
 | |
|     def build_release(self, directory):
 | |
|         for src in self.src:
 | |
|             logging.debug('Compiling %s', src)
 | |
|             execute(
 | |
|                 [self.ctx.compiler, '-c', os.path.join(self.ctx.src_dir, src),
 | |
|                  '-o', src + '.o'] + self.inc +
 | |
|                 self.ctx.shared_library_c_flags(True),
 | |
|                 cwd=directory)
 | |
|         logging.debug('Linking %s', self.name)
 | |
|         execute(
 | |
|             [self.ctx.compiler] + [src + '.o' for src in self.src] +
 | |
|             ['-o', self.name] + self.lib +
 | |
|             self.ctx.shared_library_ld_flags(True, self.name),
 | |
|             cwd=directory)
 | |
| 
 | |
| 
 | |
| def create_shared_library(name, toolset):
 | |
|     return SharedLibrary(name, toolset)
 |