mirror of
https://github.com/cea-sec/miasm
synced 2026-06-21 13:48:18 +00:00
437 lines
16 KiB
Python
437 lines
16 KiB
Python
#! /usr/bin/env python2
|
|
|
|
from __future__ import print_function
|
|
# Reference: https://stackoverflow.com/a/13468644/1806760
|
|
from setuptools import setup, Extension
|
|
from distutils.util import get_platform
|
|
from distutils.sysconfig import get_python_lib, get_config_vars
|
|
from distutils.dist import DistributionMetadata
|
|
from distutils.command.install_data import install_data
|
|
import subprocess
|
|
from tempfile import TemporaryFile
|
|
import fnmatch
|
|
import io
|
|
import os
|
|
import platform
|
|
from shutil import copy2, copyfile, rmtree, which
|
|
import sys
|
|
import tempfile
|
|
import atexit
|
|
import re
|
|
|
|
is_win = platform.system() == "Windows"
|
|
is_mac = platform.system() == "Darwin"
|
|
is_64bit = platform.architecture()[0] == "64bit"
|
|
if is_win:
|
|
import winreg
|
|
|
|
def set_extension_compile_args(extension):
|
|
rel_lib_path = extension.name.replace(".", "/")
|
|
abs_lib_path = os.path.join(get_python_lib(), rel_lib_path)
|
|
lib_name = abs_lib_path + ".so"
|
|
extension.extra_link_args = [ "-Wl,-install_name," + lib_name]
|
|
|
|
class smart_install_data(install_data):
|
|
"""Replacement for distutils.command.install_data to handle
|
|
configuration files location.
|
|
"""
|
|
def run(self):
|
|
# install files to /etc when target was /usr(/local)/etc
|
|
self.data_files = [
|
|
(path, files) for path, files in self.data_files
|
|
if path # skip README.md or any file with an empty path
|
|
]
|
|
return install_data.run(self)
|
|
|
|
def win_get_llvm_reg():
|
|
REG_PATH = "SOFTWARE\\LLVM\\LLVM"
|
|
try:
|
|
return winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE, REG_PATH, 0, winreg.KEY_READ | winreg.KEY_WOW64_32KEY)
|
|
except FileNotFoundError:
|
|
pass
|
|
return winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE, REG_PATH, 0, winreg.KEY_READ)
|
|
|
|
def win_find_clang_path():
|
|
try:
|
|
with win_get_llvm_reg() as rkey:
|
|
return winreg.QueryValueEx(rkey, None)[0]
|
|
except FileNotFoundError:
|
|
# Visual Studio ships with an optional Clang distribution, try to detect it
|
|
clang_cl = which("clang-cl")
|
|
if clang_cl is None:
|
|
return None
|
|
return os.path.abspath(os.path.join(os.path.dirname(clang_cl), "..", ".."))
|
|
|
|
def win_get_clang_version(clang_path):
|
|
try:
|
|
clang_cl = os.path.join(clang_path, "bin", "clang.exe")
|
|
stdout = subprocess.check_output("\"{}\" --version".format(clang_cl))
|
|
version = stdout.splitlines(False)[0].decode()
|
|
match = re.search(r"version (\d+\.\d+\.\d+)", version)
|
|
if match is None:
|
|
return None
|
|
version = list(map(lambda s: int(s), match.group(1).split(".")))
|
|
return version
|
|
except FileNotFoundError:
|
|
return None
|
|
|
|
def win_use_clang():
|
|
# To force python to use clang we copy the binaries in a temporary directory that's added to the PATH.
|
|
# We could use the build directory created by distutils for this, but it seems non-trivial to gather
|
|
# (https://stackoverflow.com/questions/12896367/reliable-way-to-get-the-build-directory-from-within-setup-py).
|
|
|
|
clang_path = win_find_clang_path()
|
|
if clang_path is None:
|
|
return False
|
|
clang_version = win_get_clang_version(clang_path)
|
|
if clang_version is None:
|
|
return False
|
|
tmpdir = tempfile.mkdtemp(prefix="llvm")
|
|
|
|
copyfile(os.path.join(clang_path, "bin", "clang-cl.exe"), os.path.join(tmpdir, "cl.exe"))
|
|
|
|
# If you run the installation from a Visual Studio command prompt link.exe will already exist
|
|
# Fall back to LLVM's lld-link.exe which is compatible with link's command line
|
|
if True:#which("link") is None:
|
|
# LLVM >= 14.0.0 started supporting the /LTCG flag
|
|
# Earlier versions will error during the linking phase so bail out now
|
|
if clang_version[0] < 14:
|
|
return False
|
|
copyfile(os.path.join(clang_path, "bin", "lld-link.exe"), os.path.join(tmpdir, "link.exe"))
|
|
|
|
# Add the temporary directory at the front of the PATH and clean up on exit
|
|
os.environ["PATH"] = "%s;%s" % (tmpdir, os.environ["PATH"])
|
|
atexit.register(lambda dir_: rmtree(dir_), tmpdir)
|
|
print("Found Clang {}.{}.{}: {}".format(clang_version[0], clang_version[1], clang_version[2], clang_path))
|
|
return True
|
|
|
|
build_extensions = True
|
|
build_warnings = []
|
|
win_force_clang = False
|
|
if is_win:
|
|
if is_64bit or which("cl") is None:
|
|
# We do not change to clang if under 32 bits, because even with Clang we
|
|
# do not use uint128_t with the 32 bits ABI. Regardless we can try to
|
|
# find it when building in 32-bit mode if cl.exe was not found in the PATH.
|
|
win_force_clang = win_use_clang()
|
|
if is_64bit and not win_force_clang:
|
|
build_warnings.append("Could not find a suitable Clang/LLVM installation. You can download LLVM from https://releases.llvm.org")
|
|
build_warnings.append("Alternatively you can select the 'C++ Clang-cl build tools' in the Visual Studio Installer")
|
|
build_extensions = False
|
|
cl = which("cl")
|
|
link = which("link")
|
|
if cl is None or link is None:
|
|
build_warnings.append("Could not find cl.exe and/or link.exe in the PATH, try building miasm from a Visual Studio command prompt")
|
|
build_warnings.append("More information at: https://wiki.python.org/moin/WindowsCompilers")
|
|
build_extensions = False
|
|
else:
|
|
print("Found cl.exe: {}".format(cl))
|
|
print("Found link.exe: {}".format(link))
|
|
|
|
def build_all():
|
|
packages=[
|
|
"miasm",
|
|
"miasm/arch",
|
|
"miasm/arch/x86",
|
|
"miasm/arch/arm",
|
|
"miasm/arch/aarch64",
|
|
"miasm/arch/msp430",
|
|
"miasm/arch/mep",
|
|
"miasm/arch/sh4",
|
|
"miasm/arch/mips32",
|
|
"miasm/arch/ppc",
|
|
"miasm/core",
|
|
"miasm/expression",
|
|
"miasm/ir",
|
|
"miasm/ir/translators",
|
|
"miasm/analysis",
|
|
"miasm/os_dep",
|
|
"miasm/os_dep/linux",
|
|
"miasm/loader",
|
|
"miasm/jitter",
|
|
"miasm/jitter/arch",
|
|
"miasm/jitter/loader",
|
|
]
|
|
ext_modules_all = [
|
|
Extension(
|
|
"miasm.jitter.VmMngr",
|
|
[
|
|
"miasm/jitter/vm_mngr.c",
|
|
"miasm/jitter/vm_mngr_py.c",
|
|
"miasm/jitter/bn.c",
|
|
]
|
|
),
|
|
Extension(
|
|
"miasm.jitter.arch.JitCore_x86",
|
|
[
|
|
"miasm/jitter/JitCore.c",
|
|
"miasm/jitter/vm_mngr.c",
|
|
"miasm/jitter/vm_mngr_py.c",
|
|
"miasm/jitter/op_semantics.c",
|
|
"miasm/jitter/bn.c",
|
|
"miasm/jitter/arch/JitCore_x86.c"
|
|
]
|
|
),
|
|
Extension(
|
|
"miasm.jitter.arch.JitCore_arm",
|
|
[
|
|
"miasm/jitter/JitCore.c",
|
|
"miasm/jitter/vm_mngr.c",
|
|
"miasm/jitter/vm_mngr_py.c",
|
|
"miasm/jitter/op_semantics.c",
|
|
"miasm/jitter/bn.c",
|
|
"miasm/jitter/arch/JitCore_arm.c"
|
|
]
|
|
),
|
|
Extension(
|
|
"miasm.jitter.arch.JitCore_aarch64",
|
|
[
|
|
"miasm/jitter/JitCore.c",
|
|
"miasm/jitter/vm_mngr.c",
|
|
"miasm/jitter/vm_mngr_py.c",
|
|
"miasm/jitter/op_semantics.c",
|
|
"miasm/jitter/bn.c",
|
|
"miasm/jitter/arch/JitCore_aarch64.c"
|
|
]
|
|
),
|
|
Extension(
|
|
"miasm.jitter.arch.JitCore_msp430",
|
|
[
|
|
"miasm/jitter/JitCore.c",
|
|
"miasm/jitter/vm_mngr.c",
|
|
"miasm/jitter/vm_mngr_py.c",
|
|
"miasm/jitter/op_semantics.c",
|
|
"miasm/jitter/bn.c",
|
|
"miasm/jitter/arch/JitCore_msp430.c"
|
|
]
|
|
),
|
|
Extension(
|
|
"miasm.jitter.arch.JitCore_mep",
|
|
[
|
|
"miasm/jitter/JitCore.c",
|
|
"miasm/jitter/vm_mngr.c",
|
|
"miasm/jitter/vm_mngr_py.c",
|
|
"miasm/jitter/bn.c",
|
|
"miasm/jitter/arch/JitCore_mep.c"
|
|
]
|
|
),
|
|
Extension(
|
|
"miasm.jitter.arch.JitCore_mips32",
|
|
[
|
|
"miasm/jitter/JitCore.c",
|
|
"miasm/jitter/vm_mngr.c",
|
|
"miasm/jitter/vm_mngr_py.c",
|
|
"miasm/jitter/op_semantics.c",
|
|
"miasm/jitter/bn.c",
|
|
"miasm/jitter/arch/JitCore_mips32.c"
|
|
]
|
|
),
|
|
Extension(
|
|
"miasm.jitter.arch.JitCore_ppc32",
|
|
[
|
|
"miasm/jitter/JitCore.c",
|
|
"miasm/jitter/vm_mngr.c",
|
|
"miasm/jitter/vm_mngr_py.c",
|
|
"miasm/jitter/op_semantics.c",
|
|
"miasm/jitter/bn.c",
|
|
"miasm/jitter/arch/JitCore_ppc32.c"
|
|
],
|
|
depends=[
|
|
"miasm/jitter/arch/JitCore_ppc32.h",
|
|
"miasm/jitter/arch/JitCore_ppc32_regs.h",
|
|
"miasm/jitter/bn.h",
|
|
]
|
|
),
|
|
Extension(
|
|
"miasm.jitter.arch.JitCore_m68k",
|
|
[
|
|
"miasm/jitter/JitCore.c",
|
|
"miasm/jitter/vm_mngr.c",
|
|
"miasm/jitter/vm_mngr_py.c",
|
|
"miasm/jitter/op_semantics.c",
|
|
"miasm/jitter/bn.c",
|
|
"miasm/jitter/arch/JitCore_m68k.c"
|
|
]
|
|
),
|
|
Extension(
|
|
"miasm.jitter.Jitllvm",
|
|
[
|
|
"miasm/jitter/Jitllvm.c",
|
|
"miasm/jitter/bn.c",
|
|
"miasm/runtime/udivmodti4.c",
|
|
"miasm/runtime/divti3.c",
|
|
"miasm/runtime/udivti3.c"
|
|
],
|
|
depends=[
|
|
"miasm/runtime/export.h",
|
|
"miasm/runtime/int_endianness.h",
|
|
"miasm/runtime/int_lib.h",
|
|
"miasm/runtime/int_types.h",
|
|
"miasm/runtime/int_util.h",
|
|
]
|
|
),
|
|
Extension("miasm.jitter.Jitgcc",
|
|
["miasm/jitter/Jitgcc.c",
|
|
"miasm/jitter/bn.c",
|
|
]),
|
|
]
|
|
|
|
if is_win:
|
|
# Force setuptools to use whatever msvc version installed
|
|
# https://docs.python.org/3/distutils/apiref.html#module-distutils.msvccompiler
|
|
os.environ["MSSdk"] = "1"
|
|
os.environ["DISTUTILS_USE_SDK"] = "1"
|
|
extra_compile_args = ["-D_CRT_SECURE_NO_WARNINGS"]
|
|
if win_force_clang:
|
|
march = "-m64" if is_64bit else "-m32"
|
|
extra_compile_args += [
|
|
march,
|
|
"-Wno-unused-command-line-argument",
|
|
"-Wno-visibility",
|
|
"-Wno-dll-attribute-on-redeclaration",
|
|
"-Wno-tautological-compare",
|
|
"-Wno-unused-but-set-variable",
|
|
]
|
|
for extension in ext_modules_all:
|
|
extension.extra_compile_args = extra_compile_args
|
|
elif is_mac:
|
|
for extension in ext_modules_all:
|
|
set_extension_compile_args(extension)
|
|
cfg_vars = get_config_vars()
|
|
cfg_vars["LDSHARED"] = cfg_vars["LDSHARED"].replace("-bundle", "-dynamiclib")
|
|
|
|
# Do not attempt to build the extensions when disabled
|
|
if not build_extensions:
|
|
ext_modules_all = []
|
|
|
|
print("building")
|
|
if not os.path.exists("build"):
|
|
os.mkdir("build")
|
|
build_ok = False
|
|
for name, ext_modules in [("all", ext_modules_all)]:
|
|
print("build with", repr(name))
|
|
try:
|
|
s = setup(
|
|
name = "miasm",
|
|
version = "0.1.5",
|
|
packages = packages,
|
|
data_files=[("", ["README.md"])],
|
|
package_data = {
|
|
"miasm": [
|
|
"jitter/*.h",
|
|
"jitter/arch/*.h",
|
|
"runtime/*.h",
|
|
"VERSION"
|
|
]
|
|
},
|
|
install_requires=["future", "pyparsing>=2.4.1"],
|
|
cmdclass={"install_data": smart_install_data},
|
|
ext_modules = ext_modules,
|
|
# Metadata
|
|
author = "Fabrice Desclaux",
|
|
author_email = "serpilliere@droid-corp.org",
|
|
description = "Machine code manipulation library",
|
|
license = "GPLv2",
|
|
long_description=long_description,
|
|
long_description_content_type=long_description_content_type,
|
|
keywords = [
|
|
"reverse engineering",
|
|
"disassembler",
|
|
"emulator",
|
|
"symbolic execution",
|
|
"intermediate representation",
|
|
"assembler",
|
|
],
|
|
classifiers=[
|
|
"Programming Language :: Python :: 2",
|
|
"Programming Language :: Python :: 3",
|
|
"Programming Language :: Python :: 2.7",
|
|
"Programming Language :: Python :: 3.6",
|
|
],
|
|
url = "http://miasm.re",
|
|
)
|
|
except SystemExit as e:
|
|
print(repr(e))
|
|
continue
|
|
build_ok = True
|
|
break
|
|
if not build_ok:
|
|
if len(build_warnings) > 0:
|
|
print("ERROR: There was an issue setting up the build environment:")
|
|
for warning in build_warnings:
|
|
print(" " + warning)
|
|
raise ValueError("Unable to build Miasm!")
|
|
print("build", name)
|
|
# we copy libraries from build dir to current miasm directory
|
|
build_base = "build"
|
|
if "build" in s.command_options:
|
|
if "build_base" in s.command_options["build"]:
|
|
build_base = s.command_options["build"]["build_base"]
|
|
|
|
print(build_base)
|
|
if is_win and build_extensions:
|
|
libs = []
|
|
for root, _, files in os.walk(build_base):
|
|
for filename in files:
|
|
if not filename.endswith(".lib"):
|
|
continue
|
|
f_path = os.path.join(root, filename)
|
|
libs.append(f_path)
|
|
|
|
lib_dirname = None
|
|
for dirname in os.listdir(build_base):
|
|
if not dirname.startswith("lib"):
|
|
continue
|
|
lib_dirname = dirname
|
|
break
|
|
|
|
jitters = []
|
|
for lib in libs:
|
|
filename = os.path.basename(lib)
|
|
dst = os.path.join(build_base, lib_dirname, "miasm", "jitter")
|
|
# Windows built libraries may have a name like VmMngr.cp38-win_amd64.lib
|
|
if not any([fnmatch.fnmatch(filename, pattern) for pattern in ["VmMngr.*lib", "Jitgcc.*lib", "Jitllvm.*lib"]]):
|
|
dst = os.path.join(dst, "arch")
|
|
dst = os.path.join(dst, filename)
|
|
if not os.path.isfile(dst):
|
|
print("Copying", lib, "to", dst)
|
|
copy2(lib, dst)
|
|
|
|
# Inform the user about the skipped build
|
|
if not build_extensions:
|
|
print("WARNING: miasm jit extensions were not compiled, details:")
|
|
for warning in build_warnings:
|
|
print(" " + warning)
|
|
|
|
with io.open(os.path.join(os.path.abspath(os.path.dirname("__file__")),
|
|
"README.md"), encoding="utf-8") as fdesc:
|
|
long_description = fdesc.read()
|
|
long_description_content_type = "text/markdown"
|
|
|
|
|
|
# Monkey patching (distutils does not handle Description-Content-Type
|
|
# from long_description_content_type parameter in setup()).
|
|
_write_pkg_file_orig = DistributionMetadata.write_pkg_file
|
|
|
|
|
|
def _write_pkg_file(self, file):
|
|
with TemporaryFile(mode="w+", encoding="utf-8") as tmpfd:
|
|
_write_pkg_file_orig(self, tmpfd)
|
|
tmpfd.seek(0)
|
|
for line in tmpfd:
|
|
if line.startswith("Metadata-Version: "):
|
|
file.write("Metadata-Version: 2.1\n")
|
|
elif line.startswith("Description: "):
|
|
file.write("Description-Content-Type: %s; charset=UTF-8\n" %
|
|
long_description_content_type)
|
|
file.write(line)
|
|
else:
|
|
file.write(line)
|
|
|
|
|
|
DistributionMetadata.write_pkg_file = _write_pkg_file
|
|
|
|
|
|
build_all()
|
|
|