1*a9ac8606Spatrick# -*- coding: utf-8 -*- 2*a9ac8606Spatrick# Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 3*a9ac8606Spatrick# See https://llvm.org/LICENSE.txt for license information. 4*a9ac8606Spatrick# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 5*a9ac8606Spatrick""" This module compiles the intercept library. """ 6*a9ac8606Spatrick 7*a9ac8606Spatrickimport sys 8*a9ac8606Spatrickimport os 9*a9ac8606Spatrickimport os.path 10*a9ac8606Spatrickimport re 11*a9ac8606Spatrickimport tempfile 12*a9ac8606Spatrickimport shutil 13*a9ac8606Spatrickimport contextlib 14*a9ac8606Spatrickimport logging 15*a9ac8606Spatrick 16*a9ac8606Spatrick__all__ = ['build_libear'] 17*a9ac8606Spatrick 18*a9ac8606Spatrick 19*a9ac8606Spatrickdef build_libear(compiler, dst_dir): 20*a9ac8606Spatrick """ Returns the full path to the 'libear' library. """ 21*a9ac8606Spatrick 22*a9ac8606Spatrick try: 23*a9ac8606Spatrick src_dir = os.path.dirname(os.path.realpath(__file__)) 24*a9ac8606Spatrick toolset = make_toolset(src_dir) 25*a9ac8606Spatrick toolset.set_compiler(compiler) 26*a9ac8606Spatrick toolset.set_language_standard('c99') 27*a9ac8606Spatrick toolset.add_definitions(['-D_GNU_SOURCE']) 28*a9ac8606Spatrick 29*a9ac8606Spatrick configure = do_configure(toolset) 30*a9ac8606Spatrick configure.check_function_exists('execve', 'HAVE_EXECVE') 31*a9ac8606Spatrick configure.check_function_exists('execv', 'HAVE_EXECV') 32*a9ac8606Spatrick configure.check_function_exists('execvpe', 'HAVE_EXECVPE') 33*a9ac8606Spatrick configure.check_function_exists('execvp', 'HAVE_EXECVP') 34*a9ac8606Spatrick configure.check_function_exists('execvP', 'HAVE_EXECVP2') 35*a9ac8606Spatrick configure.check_function_exists('exect', 'HAVE_EXECT') 36*a9ac8606Spatrick configure.check_function_exists('execl', 'HAVE_EXECL') 37*a9ac8606Spatrick configure.check_function_exists('execlp', 'HAVE_EXECLP') 38*a9ac8606Spatrick configure.check_function_exists('execle', 'HAVE_EXECLE') 39*a9ac8606Spatrick configure.check_function_exists('posix_spawn', 'HAVE_POSIX_SPAWN') 40*a9ac8606Spatrick configure.check_function_exists('posix_spawnp', 'HAVE_POSIX_SPAWNP') 41*a9ac8606Spatrick configure.check_symbol_exists('_NSGetEnviron', 'crt_externs.h', 42*a9ac8606Spatrick 'HAVE_NSGETENVIRON') 43*a9ac8606Spatrick configure.write_by_template( 44*a9ac8606Spatrick os.path.join(src_dir, 'config.h.in'), 45*a9ac8606Spatrick os.path.join(dst_dir, 'config.h')) 46*a9ac8606Spatrick 47*a9ac8606Spatrick target = create_shared_library('ear', toolset) 48*a9ac8606Spatrick target.add_include(dst_dir) 49*a9ac8606Spatrick target.add_sources('ear.c') 50*a9ac8606Spatrick target.link_against(toolset.dl_libraries()) 51*a9ac8606Spatrick target.link_against(['pthread']) 52*a9ac8606Spatrick target.build_release(dst_dir) 53*a9ac8606Spatrick 54*a9ac8606Spatrick return os.path.join(dst_dir, target.name) 55*a9ac8606Spatrick 56*a9ac8606Spatrick except Exception: 57*a9ac8606Spatrick logging.info("Could not build interception library.", exc_info=True) 58*a9ac8606Spatrick return None 59*a9ac8606Spatrick 60*a9ac8606Spatrick 61*a9ac8606Spatrickdef execute(cmd, *args, **kwargs): 62*a9ac8606Spatrick """ Make subprocess execution silent. """ 63*a9ac8606Spatrick 64*a9ac8606Spatrick import subprocess 65*a9ac8606Spatrick kwargs.update({'stdout': subprocess.PIPE, 'stderr': subprocess.STDOUT}) 66*a9ac8606Spatrick return subprocess.check_call(cmd, *args, **kwargs) 67*a9ac8606Spatrick 68*a9ac8606Spatrick 69*a9ac8606Spatrick@contextlib.contextmanager 70*a9ac8606Spatrickdef TemporaryDirectory(**kwargs): 71*a9ac8606Spatrick name = tempfile.mkdtemp(**kwargs) 72*a9ac8606Spatrick try: 73*a9ac8606Spatrick yield name 74*a9ac8606Spatrick finally: 75*a9ac8606Spatrick shutil.rmtree(name) 76*a9ac8606Spatrick 77*a9ac8606Spatrick 78*a9ac8606Spatrickclass Toolset(object): 79*a9ac8606Spatrick """ Abstract class to represent different toolset. """ 80*a9ac8606Spatrick 81*a9ac8606Spatrick def __init__(self, src_dir): 82*a9ac8606Spatrick self.src_dir = src_dir 83*a9ac8606Spatrick self.compiler = None 84*a9ac8606Spatrick self.c_flags = [] 85*a9ac8606Spatrick 86*a9ac8606Spatrick def set_compiler(self, compiler): 87*a9ac8606Spatrick """ part of public interface """ 88*a9ac8606Spatrick self.compiler = compiler 89*a9ac8606Spatrick 90*a9ac8606Spatrick def set_language_standard(self, standard): 91*a9ac8606Spatrick """ part of public interface """ 92*a9ac8606Spatrick self.c_flags.append('-std=' + standard) 93*a9ac8606Spatrick 94*a9ac8606Spatrick def add_definitions(self, defines): 95*a9ac8606Spatrick """ part of public interface """ 96*a9ac8606Spatrick self.c_flags.extend(defines) 97*a9ac8606Spatrick 98*a9ac8606Spatrick def dl_libraries(self): 99*a9ac8606Spatrick raise NotImplementedError() 100*a9ac8606Spatrick 101*a9ac8606Spatrick def shared_library_name(self, name): 102*a9ac8606Spatrick raise NotImplementedError() 103*a9ac8606Spatrick 104*a9ac8606Spatrick def shared_library_c_flags(self, release): 105*a9ac8606Spatrick extra = ['-DNDEBUG', '-O3'] if release else [] 106*a9ac8606Spatrick return extra + ['-fPIC'] + self.c_flags 107*a9ac8606Spatrick 108*a9ac8606Spatrick def shared_library_ld_flags(self, release, name): 109*a9ac8606Spatrick raise NotImplementedError() 110*a9ac8606Spatrick 111*a9ac8606Spatrick 112*a9ac8606Spatrickclass DarwinToolset(Toolset): 113*a9ac8606Spatrick def __init__(self, src_dir): 114*a9ac8606Spatrick Toolset.__init__(self, src_dir) 115*a9ac8606Spatrick 116*a9ac8606Spatrick def dl_libraries(self): 117*a9ac8606Spatrick return [] 118*a9ac8606Spatrick 119*a9ac8606Spatrick def shared_library_name(self, name): 120*a9ac8606Spatrick return 'lib' + name + '.dylib' 121*a9ac8606Spatrick 122*a9ac8606Spatrick def shared_library_ld_flags(self, release, name): 123*a9ac8606Spatrick extra = ['-dead_strip'] if release else [] 124*a9ac8606Spatrick return extra + ['-dynamiclib', '-install_name', '@rpath/' + name] 125*a9ac8606Spatrick 126*a9ac8606Spatrick 127*a9ac8606Spatrickclass UnixToolset(Toolset): 128*a9ac8606Spatrick def __init__(self, src_dir): 129*a9ac8606Spatrick Toolset.__init__(self, src_dir) 130*a9ac8606Spatrick 131*a9ac8606Spatrick def dl_libraries(self): 132*a9ac8606Spatrick return [] 133*a9ac8606Spatrick 134*a9ac8606Spatrick def shared_library_name(self, name): 135*a9ac8606Spatrick return 'lib' + name + '.so' 136*a9ac8606Spatrick 137*a9ac8606Spatrick def shared_library_ld_flags(self, release, name): 138*a9ac8606Spatrick extra = [] if release else [] 139*a9ac8606Spatrick return extra + ['-shared', '-Wl,-soname,' + name] 140*a9ac8606Spatrick 141*a9ac8606Spatrick 142*a9ac8606Spatrickclass LinuxToolset(UnixToolset): 143*a9ac8606Spatrick def __init__(self, src_dir): 144*a9ac8606Spatrick UnixToolset.__init__(self, src_dir) 145*a9ac8606Spatrick 146*a9ac8606Spatrick def dl_libraries(self): 147*a9ac8606Spatrick return ['dl'] 148*a9ac8606Spatrick 149*a9ac8606Spatrick 150*a9ac8606Spatrickdef make_toolset(src_dir): 151*a9ac8606Spatrick platform = sys.platform 152*a9ac8606Spatrick if platform in {'win32', 'cygwin'}: 153*a9ac8606Spatrick raise RuntimeError('not implemented on this platform') 154*a9ac8606Spatrick elif platform == 'darwin': 155*a9ac8606Spatrick return DarwinToolset(src_dir) 156*a9ac8606Spatrick elif platform in {'linux', 'linux2'}: 157*a9ac8606Spatrick return LinuxToolset(src_dir) 158*a9ac8606Spatrick else: 159*a9ac8606Spatrick return UnixToolset(src_dir) 160*a9ac8606Spatrick 161*a9ac8606Spatrick 162*a9ac8606Spatrickclass Configure(object): 163*a9ac8606Spatrick def __init__(self, toolset): 164*a9ac8606Spatrick self.ctx = toolset 165*a9ac8606Spatrick self.results = {'APPLE': sys.platform == 'darwin'} 166*a9ac8606Spatrick 167*a9ac8606Spatrick def _try_to_compile_and_link(self, source): 168*a9ac8606Spatrick try: 169*a9ac8606Spatrick with TemporaryDirectory() as work_dir: 170*a9ac8606Spatrick src_file = 'check.c' 171*a9ac8606Spatrick with open(os.path.join(work_dir, src_file), 'w') as handle: 172*a9ac8606Spatrick handle.write(source) 173*a9ac8606Spatrick 174*a9ac8606Spatrick execute([self.ctx.compiler, src_file] + self.ctx.c_flags, 175*a9ac8606Spatrick cwd=work_dir) 176*a9ac8606Spatrick return True 177*a9ac8606Spatrick except Exception: 178*a9ac8606Spatrick return False 179*a9ac8606Spatrick 180*a9ac8606Spatrick def check_function_exists(self, function, name): 181*a9ac8606Spatrick template = "int FUNCTION(); int main() { return FUNCTION(); }" 182*a9ac8606Spatrick source = template.replace("FUNCTION", function) 183*a9ac8606Spatrick 184*a9ac8606Spatrick logging.debug('Checking function %s', function) 185*a9ac8606Spatrick found = self._try_to_compile_and_link(source) 186*a9ac8606Spatrick logging.debug('Checking function %s -- %s', function, 187*a9ac8606Spatrick 'found' if found else 'not found') 188*a9ac8606Spatrick self.results.update({name: found}) 189*a9ac8606Spatrick 190*a9ac8606Spatrick def check_symbol_exists(self, symbol, include, name): 191*a9ac8606Spatrick template = """#include <INCLUDE> 192*a9ac8606Spatrick int main() { return ((int*)(&SYMBOL))[0]; }""" 193*a9ac8606Spatrick source = template.replace('INCLUDE', include).replace("SYMBOL", symbol) 194*a9ac8606Spatrick 195*a9ac8606Spatrick logging.debug('Checking symbol %s', symbol) 196*a9ac8606Spatrick found = self._try_to_compile_and_link(source) 197*a9ac8606Spatrick logging.debug('Checking symbol %s -- %s', symbol, 198*a9ac8606Spatrick 'found' if found else 'not found') 199*a9ac8606Spatrick self.results.update({name: found}) 200*a9ac8606Spatrick 201*a9ac8606Spatrick def write_by_template(self, template, output): 202*a9ac8606Spatrick def transform(line, definitions): 203*a9ac8606Spatrick 204*a9ac8606Spatrick pattern = re.compile(r'^#cmakedefine\s+(\S+)') 205*a9ac8606Spatrick m = pattern.match(line) 206*a9ac8606Spatrick if m: 207*a9ac8606Spatrick key = m.group(1) 208*a9ac8606Spatrick if key not in definitions or not definitions[key]: 209*a9ac8606Spatrick return '/* #undef {0} */{1}'.format(key, os.linesep) 210*a9ac8606Spatrick else: 211*a9ac8606Spatrick return '#define {0}{1}'.format(key, os.linesep) 212*a9ac8606Spatrick return line 213*a9ac8606Spatrick 214*a9ac8606Spatrick with open(template, 'r') as src_handle: 215*a9ac8606Spatrick logging.debug('Writing config to %s', output) 216*a9ac8606Spatrick with open(output, 'w') as dst_handle: 217*a9ac8606Spatrick for line in src_handle: 218*a9ac8606Spatrick dst_handle.write(transform(line, self.results)) 219*a9ac8606Spatrick 220*a9ac8606Spatrick 221*a9ac8606Spatrickdef do_configure(toolset): 222*a9ac8606Spatrick return Configure(toolset) 223*a9ac8606Spatrick 224*a9ac8606Spatrick 225*a9ac8606Spatrickclass SharedLibrary(object): 226*a9ac8606Spatrick def __init__(self, name, toolset): 227*a9ac8606Spatrick self.name = toolset.shared_library_name(name) 228*a9ac8606Spatrick self.ctx = toolset 229*a9ac8606Spatrick self.inc = [] 230*a9ac8606Spatrick self.src = [] 231*a9ac8606Spatrick self.lib = [] 232*a9ac8606Spatrick 233*a9ac8606Spatrick def add_include(self, directory): 234*a9ac8606Spatrick self.inc.extend(['-I', directory]) 235*a9ac8606Spatrick 236*a9ac8606Spatrick def add_sources(self, source): 237*a9ac8606Spatrick self.src.append(source) 238*a9ac8606Spatrick 239*a9ac8606Spatrick def link_against(self, libraries): 240*a9ac8606Spatrick self.lib.extend(['-l' + lib for lib in libraries]) 241*a9ac8606Spatrick 242*a9ac8606Spatrick def build_release(self, directory): 243*a9ac8606Spatrick for src in self.src: 244*a9ac8606Spatrick logging.debug('Compiling %s', src) 245*a9ac8606Spatrick execute( 246*a9ac8606Spatrick [self.ctx.compiler, '-c', os.path.join(self.ctx.src_dir, src), 247*a9ac8606Spatrick '-o', src + '.o'] + self.inc + 248*a9ac8606Spatrick self.ctx.shared_library_c_flags(True), 249*a9ac8606Spatrick cwd=directory) 250*a9ac8606Spatrick logging.debug('Linking %s', self.name) 251*a9ac8606Spatrick execute( 252*a9ac8606Spatrick [self.ctx.compiler] + [src + '.o' for src in self.src] + 253*a9ac8606Spatrick ['-o', self.name] + self.lib + 254*a9ac8606Spatrick self.ctx.shared_library_ld_flags(True, self.name), 255*a9ac8606Spatrick cwd=directory) 256*a9ac8606Spatrick 257*a9ac8606Spatrick 258*a9ac8606Spatrickdef create_shared_library(name, toolset): 259*a9ac8606Spatrick return SharedLibrary(name, toolset) 260