brintos

brintos / llvm-project-archived public Read only

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