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