From 1bd2a32f2afc6e08225d7491b82619cf4c8e87d9 Mon Sep 17 00:00:00 2001 From: Kevin Phoenix Date: Tue, 23 Jul 2024 23:18:26 -0700 Subject: [PATCH] Remove deprecated packaging and cython (#2400) * Remove deprecated packaging and cython * Fix some issues after cherry-picking * Fix CI issues * Remove test files added for v6 * Also remove test_alpha.py --- .github/workflows/CITest.yml | 39 +- .github/workflows/fuzz.yml | 2 + .github/workflows/python-tests.yml | 32 ++ .gitignore | 1 + bindings/python/BUILDING.txt | 40 +- bindings/python/Makefile | 19 +- bindings/python/capstone/__init__.py | 16 +- bindings/python/pyproject.toml | 3 + bindings/python/pyx/README | 1 - bindings/python/pyx/ccapstone.pxd | 72 ---- bindings/python/pyx/ccapstone.pyx | 386 ------------------ bindings/python/setup.py | 35 +- bindings/python/setup_cython.py | 151 ------- bindings/python/{ => tests}/test_all.py | 0 bindings/python/{ => tests}/test_arm.py | 0 bindings/python/{ => tests}/test_arm64.py | 0 bindings/python/{ => tests}/test_basic.py | 0 bindings/python/{ => tests}/test_bpf.py | 0 .../{ => tests}/test_customized_mnem.py | 0 bindings/python/{ => tests}/test_detail.py | 0 bindings/python/{ => tests}/test_evm.py | 0 bindings/python/{ => tests}/test_lite.py | 0 bindings/python/{ => tests}/test_m680x.py | 0 bindings/python/{ => tests}/test_m68k.py | 0 bindings/python/{ => tests}/test_mips.py | 0 bindings/python/{ => tests}/test_mos65xx.py | 0 bindings/python/{ => tests}/test_ppc.py | 0 bindings/python/{ => tests}/test_riscv.py | 0 bindings/python/{ => tests}/test_sh.py | 0 bindings/python/{ => tests}/test_skipdata.py | 0 bindings/python/{ => tests}/test_sparc.py | 0 bindings/python/{ => tests}/test_systemz.py | 0 .../python/{ => tests}/test_tms320c64x.py | 0 bindings/python/{ => tests}/test_tricore.py | 0 bindings/python/{ => tests}/test_wasm.py | 0 bindings/python/{ => tests}/test_x86.py | 0 bindings/python/{ => tests}/test_xcore.py | 0 bindings/python/{ => tests}/xprint.py | 0 38 files changed, 80 insertions(+), 717 deletions(-) create mode 100644 .github/workflows/python-tests.yml create mode 100644 bindings/python/pyproject.toml delete mode 100644 bindings/python/pyx/README delete mode 100644 bindings/python/pyx/ccapstone.pxd delete mode 100644 bindings/python/pyx/ccapstone.pyx delete mode 100644 bindings/python/setup_cython.py rename bindings/python/{ => tests}/test_all.py (100%) rename bindings/python/{ => tests}/test_arm.py (100%) rename bindings/python/{ => tests}/test_arm64.py (100%) rename bindings/python/{ => tests}/test_basic.py (100%) rename bindings/python/{ => tests}/test_bpf.py (100%) rename bindings/python/{ => tests}/test_customized_mnem.py (100%) rename bindings/python/{ => tests}/test_detail.py (100%) rename bindings/python/{ => tests}/test_evm.py (100%) rename bindings/python/{ => tests}/test_lite.py (100%) rename bindings/python/{ => tests}/test_m680x.py (100%) rename bindings/python/{ => tests}/test_m68k.py (100%) rename bindings/python/{ => tests}/test_mips.py (100%) rename bindings/python/{ => tests}/test_mos65xx.py (100%) rename bindings/python/{ => tests}/test_ppc.py (100%) rename bindings/python/{ => tests}/test_riscv.py (100%) rename bindings/python/{ => tests}/test_sh.py (100%) rename bindings/python/{ => tests}/test_skipdata.py (100%) rename bindings/python/{ => tests}/test_sparc.py (100%) rename bindings/python/{ => tests}/test_systemz.py (100%) rename bindings/python/{ => tests}/test_tms320c64x.py (100%) rename bindings/python/{ => tests}/test_tricore.py (100%) rename bindings/python/{ => tests}/test_wasm.py (100%) rename bindings/python/{ => tests}/test_x86.py (100%) rename bindings/python/{ => tests}/test_xcore.py (100%) rename bindings/python/{ => tests}/xprint.py (100%) diff --git a/.github/workflows/CITest.yml b/.github/workflows/CITest.yml index 85a7e44ab6..a6224a970f 100644 --- a/.github/workflows/CITest.yml +++ b/.github/workflows/CITest.yml @@ -1,10 +1,10 @@ name: Run Test -on: +on: push: paths-ignore: - ".gitignore" - "docs/**" - - "ChangeLog" + - "ChangeLog" - "CREDITS.TXT" - "COMPILE.TXT" - "COMPILE_MSVC.TXT" @@ -30,46 +30,29 @@ jobs: matrix: config: - { - name: 'ubuntu-20.04 x64 python3.6 cmake', - os: ubuntu-20.04, - arch: x64, - python-arch: x64, - python-version: '3.6', - build-system: 'cmake', - } - - { - name: 'ubuntu-22.04 x64 python3.9 make', + name: 'ubuntu-22.04 x64 make', os: ubuntu-22.04, arch: x64, - python-arch: x64, - python-version: '3.9', build-system: 'make', - } + } - { - name: 'ubuntu-22.04 x64 python3.9 cmake', + name: 'ubuntu-22.04 x64 cmake', os: ubuntu-22.04, arch: x64, - python-arch: x64, - python-version: '3.9', build-system: 'cmake', - } + enable-asan: 'OFF' + } - { - name: 'ubuntu-22.04 x64 python3.11 cmake', - os: ubuntu-22.04, + name: 'ubuntu-22.04 x64 ASAN', + os: ubuntu-latest, arch: x64, - python-arch: x64, - python-version: '3.11', build-system: 'cmake', + enable-asan: 'ON' } steps: - uses: actions/checkout@v3 - - name: Set up Python - uses: actions/setup-python@v4 - with: - python-version: ${{ matrix.config.python-version }} - - name: prepare shell: 'script -q -e -c "bash {0}"' run: | @@ -135,7 +118,7 @@ jobs: - name: '🛠️ Win MSVC 64 setup' if: contains(matrix.config.name, 'MSVC 64') - uses: ilammy/msvc-dev-cmd@v1 + uses: ilammy/msvc-dev-cmd@v1 with: arch: 'x64' diff --git a/.github/workflows/fuzz.yml b/.github/workflows/fuzz.yml index 86fcfe451a..f74438cc66 100644 --- a/.github/workflows/fuzz.yml +++ b/.github/workflows/fuzz.yml @@ -21,3 +21,5 @@ jobs: with: name: artifacts path: ./out/artifacts + - name: Install Python Dependencies + run: python -m pip install --upgrade setuptools build wheel diff --git a/.github/workflows/python-tests.yml b/.github/workflows/python-tests.yml new file mode 100644 index 0000000000..c47993a2b2 --- /dev/null +++ b/.github/workflows/python-tests.yml @@ -0,0 +1,32 @@ +name: Python Package CI + +on: + push: + pull_request: + +jobs: + build: + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + os: [ubuntu-24.04, windows-2022, macOS-14] + python-version: ["3.8", "3.12"] + + steps: + - uses: actions/checkout@v4 + + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v5 + with: + python-version: ${{ matrix.python-version }} + + - name: Setup MSVC + if: runner.os == 'Windows' + uses: ilammy/msvc-dev-cmd@v1 + + - name: Build and install capstone + run: pip install ./bindings/python + + - name: Run tests + run: python ./bindings/python/tests/test_all.py diff --git a/.gitignore b/.gitignore index aa8f2543e4..711ce07a4f 100644 --- a/.gitignore +++ b/.gitignore @@ -25,6 +25,7 @@ # python bindings/python/build/ bindings/python/capstone.egg-info/ +bindings/cython/capstone.egg-info/ *.pyc # java diff --git a/bindings/python/BUILDING.txt b/bindings/python/BUILDING.txt index e527b153eb..420c4b9ede 100644 --- a/bindings/python/BUILDING.txt +++ b/bindings/python/BUILDING.txt @@ -13,45 +13,7 @@ To control the install destination, set the DESTDIR environment variable. -2. For better Python performance, install cython-based binding with: - - $ sudo make install_cython - - Note that this requires Cython installed first. To install Cython, see - below. - -3. To install Cython, you have to ensure that the header files - and the static library for Python are installed beforehand. - - E.g. on Ubuntu, do: - - $ sudo apt-get install python-dev - - Depending on if you already have pip or easy_install installed, install - Cython with either: - - $ sudo pip install cython - or: - $ sudo easy_install cython - - NOTE: Depending on your distribution you might also be able to - install the required Cython version using your repository. - - E.g. on Ubuntu, do: - - $ sudo apt-get install cython - - However, our cython-based binding requires Cython version 0.19 or newer, - but sometimes distributions only provide older version. Make sure to - verify the current installed version before going into section 2 above. - - E.g, on Ubuntu, you can verify the current Cython version with: - - $ apt-cache policy cython - - Which should at least print version 0.19 - -4. This directory contains some test code to show how to use the Capstone API. +2. The tests directory contains some test code to show how to use the Capstone API. - test_basic.py This code shows the most simple form of API where we only want to get basic diff --git a/bindings/python/Makefile b/bindings/python/Makefile index b901b061d1..2f13770ea8 100644 --- a/bindings/python/Makefile +++ b/bindings/python/Makefile @@ -1,7 +1,7 @@ PYTHON2 ?= python2 PYTHON3 ?= python3 -.PHONY: gen_const install install3 install_cython sdist sdist3 bdist bdist3 clean check +.PHONY: gen_const install sdist bdist clean check gen_const: cd .. && $(PYTHON2) const_generator.py python @@ -22,23 +22,6 @@ install3: $(PYTHON3) setup.py build install; \ fi -# NOTE: Newer cython can be installed by: sudo pip install --upgrade cython -install_cython: - rm -rf src/ - if test -n "${DESTDIR}"; then \ - $(PYTHON2) setup_cython.py build install --root="${DESTDIR}"; \ - else \ - $(PYTHON2) setup_cython.py build install; \ - fi - -install3_cython: - rm -rf src/ - if test -n "${DESTDIR}"; then \ - $(PYTHON3) setup_cython.py build install --root="${DESTDIR}"; \ - else \ - $(PYTHON3) setup_cython.py build install; \ - fi - # build & upload PyPi package with source code of the core sdist: rm -rf src/ dist/ diff --git a/bindings/python/capstone/__init__.py b/bindings/python/capstone/__init__.py index ffc28101af..50219cd12f 100755 --- a/bindings/python/capstone/__init__.py +++ b/bindings/python/capstone/__init__.py @@ -348,10 +348,16 @@ import ctypes, ctypes.util from os.path import split, join, dirname -import distutils.sysconfig -import pkg_resources +import sysconfig +from pathlib import PurePath import inspect + +if sys.version_info >= (3, 9): + import importlib.resources as resources +else: + import importlib_resources as resources + if not hasattr(sys.modules[__name__], '__file__'): __file__ = inspect.getfile(inspect.currentframe()) @@ -379,17 +385,17 @@ def _load_lib(path): # Loading attempts, in order # - user-provided environment variable -# - pkg_resources can get us the path to the local libraries +# - importlib.resources can get us the path to the local libraries # - we can get the path to the local libraries by parsing our filename # - global load # - python's lib directory # - last-gasp attempt at some hardcoded paths on darwin and linux _path_list = [os.getenv('LIBCAPSTONE_PATH', None), - pkg_resources.resource_filename(__name__, 'lib'), + str(resources.files(__name__) / "lib"), join(split(__file__)[0], 'lib'), '', - distutils.sysconfig.get_python_lib(), + sysconfig.get_path('platlib'), "/usr/local/lib/" if sys.platform == 'darwin' else '/usr/lib64'] for _path in _path_list: diff --git a/bindings/python/pyproject.toml b/bindings/python/pyproject.toml new file mode 100644 index 0000000000..fed528d4a7 --- /dev/null +++ b/bindings/python/pyproject.toml @@ -0,0 +1,3 @@ +[build-system] +requires = ["setuptools"] +build-backend = "setuptools.build_meta" diff --git a/bindings/python/pyx/README b/bindings/python/pyx/README deleted file mode 100644 index 2b88620150..0000000000 --- a/bindings/python/pyx/README +++ /dev/null @@ -1 +0,0 @@ -This directory contains Cython files. diff --git a/bindings/python/pyx/ccapstone.pxd b/bindings/python/pyx/ccapstone.pxd deleted file mode 100644 index 8b163f2f69..0000000000 --- a/bindings/python/pyx/ccapstone.pxd +++ /dev/null @@ -1,72 +0,0 @@ -# By Dang Hoang Vu , 2014 - -from libcpp cimport bool -from libc.stdint cimport uint8_t, uint64_t, uint16_t - -cdef extern from "": - - ctypedef size_t csh - - ctypedef enum cs_mode: - pass - - ctypedef enum cs_arch: - pass - - ctypedef struct cs_detail: - pass - - ctypedef struct cs_insn: - unsigned int id - uint64_t address - uint16_t size - uint8_t bytes[24] - char mnemonic[32] - char op_str[160] - cs_detail *detail - - ctypedef enum cs_err: - pass - - ctypedef enum cs_opt_type: - pass - - unsigned int cs_version(int *major, int *minor) - - bool cs_support(int arch) - - cs_err cs_open(cs_arch arch, cs_mode mode, csh *handle) - - cs_err cs_close(csh *handle) - - cs_err cs_errno(csh handle) - - size_t cs_disasm(csh handle, - const uint8_t *code, size_t code_size, - uint64_t address, - size_t count, - cs_insn **insn) - - cs_err cs_option(csh handle, cs_opt_type type, size_t value) - - void cs_free(cs_insn *insn, size_t count) - - const char *cs_reg_name(csh handle, unsigned int reg_id) - - const char *cs_insn_name(csh handle, unsigned int insn_id) - - const char *cs_group_name(csh handle, unsigned int group_id) - - bool cs_insn_group(csh handle, cs_insn *insn, unsigned int group_id) - - bool cs_reg_read(csh handle, cs_insn *insn, unsigned int reg_id) - - bool cs_reg_write(csh handle, cs_insn *insn, unsigned int reg_id) - - int cs_op_count(csh handle, cs_insn *insn, unsigned int op_type) - - cs_err cs_regs_access(csh handle, cs_insn *insn, uint16_t *regs_read, uint8_t *read_count, uint16_t *regs_write, uint8_t *write_count) - - int cs_op_index(csh handle, cs_insn *insn, unsigned int op_type, - unsigned int position) - diff --git a/bindings/python/pyx/ccapstone.pyx b/bindings/python/pyx/ccapstone.pyx deleted file mode 100644 index 932752c4de..0000000000 --- a/bindings/python/pyx/ccapstone.pyx +++ /dev/null @@ -1,386 +0,0 @@ -# By Dang Hoang Vu , 2014 - -cimport pyx.ccapstone as cc -import capstone, ctypes -from . import arm, x86, mips, ppc, arm64, sparc, systemz, xcore, tms320c64x, m68k, m680x, evm, mos65xx, wasm, bpf, riscv, sh, tricore, CsError - -_diet = cc.cs_support(capstone.CS_SUPPORT_DIET) - - -class CsDetail(object): - - def __init__(self, arch, raw_detail = None): - if not raw_detail: - return - detail = ctypes.cast(raw_detail, ctypes.POINTER(capstone._cs_detail)).contents - - self.regs_read = detail.regs_read - self.regs_read_count = detail.regs_read_count - self.regs_write = detail.regs_write - self.regs_write_count = detail.regs_write_count - self.groups = detail.groups - self.groups_count = detail.groups_count - self.writeback = detail.writeback - - if arch == capstone.CS_ARCH_ARM: - (self.usermode, self.vector_size, self.vector_data, self.cps_mode, self.cps_flag, \ - self.cc, self.update_flags, self.writeback, self.post_index, self.mem_barrier, self.operands) = \ - arm.get_arch_info(detail.arch.arm) - elif arch == capstone.CS_ARCH_ARM64: - (self.cc, self.update_flags, self.writeback, self.post_index, self.operands) = \ - arm64.get_arch_info(detail.arch.arm64) - elif arch == capstone.CS_ARCH_X86: - (self.prefix, self.opcode, self.rex, self.addr_size, \ - self.modrm, self.sib, self.disp, \ - self.sib_index, self.sib_scale, self.sib_base, \ - self.xop_cc, self.sse_cc, self.avx_cc, self.avx_sae, self.avx_rm, \ - self.eflags, self.fpu_flags, self.encoding, self.modrm_offset, self.disp_offset, \ - self.disp_size, self.imm_offset, self.imm_size, self.operands) = x86.get_arch_info(detail.arch.x86) - elif arch == capstone.CS_ARCH_M68K: - (self.operands, self.op_size) = m68k.get_arch_info(detail.arch.m68k) - elif arch == capstone.CS_ARCH_MIPS: - self.operands = mips.get_arch_info(detail.arch.mips) - elif arch == capstone.CS_ARCH_PPC: - (self.bc, self.bh, self.update_cr0, self.operands) = \ - ppc.get_arch_info(detail.arch.ppc) - elif arch == capstone.CS_ARCH_SPARC: - (self.cc, self.hint, self.operands) = sparc.get_arch_info(detail.arch.sparc) - elif arch == capstone.CS_ARCH_SYSZ: - (self.cc, self.operands) = systemz.get_arch_info(detail.arch.sysz) - elif arch == capstone.CS_ARCH_XCORE: - self.operands = xcore.get_arch_info(detail.arch.xcore) - elif arch == capstone.CS_ARCH_TMS320C64X: - (self.condition, self.funit, self.parallel, self.operands) = tms320c64x.get_arch_info(self._detail.arch.tms320c64x) - elif arch == capstone.CS_ARCH_M680X: - (self.flags, self.operands) = m680x.get_arch_info(detail.arch.m680x) - elif arch == capstone.CS_ARCH_EVM: - (self.pop, self.push, self.fee) = evm.get_arch_info(detail.arch.evm) - elif arch == capstone.CS_ARCH_MOS65XX: - (self.am, self.modifies_flags, self.operands) = mos65xx.get_arch_info(detail.arch.mos65xx) - elif arch == capstone.CS_ARCH_WASM: - (self.operands) = wasm.get_arch_info(detail.arch.wasm) - elif arch == capstone.CS_ARCH_BPF: - (self.operands) = bpf.get_arch_info(detail.arch.bpf) - elif arch == capstone.CS_ARCH_RISCV: - (self.need_effective_addr, self.operands) = riscv.get_arch_info(detail.arch.riscv) - elif arch == capstone.CS_ARCH_SH: - (self.sh_insn, self.sh_size, self.operands) = sh.get_arch_info(detail.arch.sh) - elif arch == capstone.CS_ARCH_TRICORE: - (self.update_flags, self.operands) = tricore.get_arch_info(detail.arch.tricore) - - -cdef class CsInsn(object): - - cdef cc.cs_insn _raw - cdef cc.csh _csh - cdef object _detail - - def __cinit__(self, _detail): - self._detail = _detail - - # defer to CsDetail structure for everything else. - def __getattr__(self, name): - _detail = self._detail - if not _detail: - raise CsError(capstone.CS_ERR_DETAIL) - return getattr(_detail, name) - - # return instruction's operands. - @property - def operands(self): - return self._detail.operands - - # return instruction's ID. - @property - def id(self): - return self._raw.id - - # return instruction's address. - @property - def address(self): - return self._raw.address - - # return instruction's size. - @property - def size(self): - return self._raw.size - - # return instruction's machine bytes (which should have @size bytes). - @property - def bytes(self): - return bytearray(self._raw.bytes[:self._raw.size]) - - # return instruction's mnemonic. - @property - def mnemonic(self): - if _diet: - # Diet engine cannot provide @mnemonic & @op_str - raise CsError(capstone.CS_ERR_DIET) - - return self._raw.mnemonic - - # return instruction's operands (in string). - @property - def op_str(self): - if _diet: - # Diet engine cannot provide @mnemonic & @op_str - raise CsError(capstone.CS_ERR_DIET) - - return self._raw.op_str - - # return list of all implicit registers being read. - @property - def regs_read(self): - if self._raw.id == 0: - raise CsError(capstone.CS_ERR_SKIPDATA) - - if _diet: - # Diet engine cannot provide @regs_read - raise CsError(capstone.CS_ERR_DIET) - - if self._detail: - detail = self._detail - return detail.regs_read[:detail.regs_read_count] - - raise CsError(capstone.CS_ERR_DETAIL) - - # return list of all implicit registers being modified - @property - def regs_write(self): - if self._raw.id == 0: - raise CsError(capstone.CS_ERR_SKIPDATA) - - if _diet: - # Diet engine cannot provide @regs_write - raise CsError(capstone.CS_ERR_DIET) - - if self._detail: - detail = self._detail - return detail.regs_write[:detail.regs_write_count] - - raise CsError(capstone.CS_ERR_DETAIL) - - # return list of semantic groups this instruction belongs to. - @property - def groups(self): - if self._raw.id == 0: - raise CsError(capstone.CS_ERR_SKIPDATA) - - if _diet: - # Diet engine cannot provide @groups - raise CsError(capstone.CS_ERR_DIET) - - if self._detail: - detail = self._detail - return detail.groups[:detail.groups_count] - - raise CsError(capstone.CS_ERR_DETAIL) - - # get the last error code - def errno(self): - return cc.cs_errno(self._csh) - - # get the register name, given the register ID - def reg_name(self, reg_id): - if self._raw.id == 0: - raise CsError(capstone.CS_ERR_SKIPDATA) - - if _diet: - # Diet engine cannot provide register's name - raise CsError(capstone.CS_ERR_DIET) - - return cc.cs_reg_name(self._csh, reg_id) - - # get the instruction string - def insn_name(self): - if _diet: - # Diet engine cannot provide instruction's name - raise CsError(capstone.CS_ERR_DIET) - - return cc.cs_insn_name(self._csh, self.id) - - # get the group string - def group_name(self, group_id): - if _diet: - # Diet engine cannot provide group's name - raise CsError(capstone.CS_ERR_DIET) - - return cc.cs_group_name(self._csh, group_id) - - # verify if this insn belong to group with id as @group_id - def group(self, group_id): - if self._raw.id == 0: - raise CsError(capstone.CS_ERR_SKIPDATA) - - if _diet: - # Diet engine cannot provide @groups - raise CsError(capstone.CS_ERR_DIET) - - return group_id in self.groups - - # verify if this instruction implicitly read register @reg_id - def reg_read(self, reg_id): - if self._raw.id == 0: - raise CsError(capstone.CS_ERR_SKIPDATA) - - if _diet: - # Diet engine cannot provide @regs_read - raise CsError(capstone.CS_ERR_DIET) - - return reg_id in self.regs_read - - # verify if this instruction implicitly modified register @reg_id - def reg_write(self, reg_id): - if self._raw.id == 0: - raise CsError(capstone.CS_ERR_SKIPDATA) - - if _diet: - # Diet engine cannot provide @regs_write - raise CsError(capstone.CS_ERR_DIET) - - return reg_id in self.regs_write - - # return number of operands having same operand type @op_type - def op_count(self, op_type): - if self._raw.id == 0: - raise CsError(capstone.CS_ERR_SKIPDATA) - - c = 0 - for op in self._detail.operands: - if op.type == op_type: - c += 1 - return c - - # get the operand at position @position of all operands having the same type @op_type - def op_find(self, op_type, position): - if self._raw.id == 0: - raise CsError(capstone.CS_ERR_SKIPDATA) - - c = 0 - for op in self._detail.operands: - if op.type == op_type: - c += 1 - if c == position: - return op - - # Return (list-of-registers-read, list-of-registers-modified) by this instructions. - # This includes all the implicit & explicit registers. - def regs_access(self): - if self._raw.id == 0: - raise CsError(capstone.CS_ERR_SKIPDATA) - - cdef cc.uint16_t regs_read[64] - cdef cc.uint16_t regs_write[64] - cdef cc.uint8_t read_count, write_count - - status = cc.cs_regs_access(self._cs.csh, &self._raw, regs_read, &read_count, regs_write, &write_count) - if status != capstone.CS_ERR_OK: - raise CsError(status) - - r1 = [] - for i from 0 <= i < read_count: r1.append(regs_read[i]) - - w1 = [] - for i from 0 <= i < write_count: w1.append(regs_write[i]) - - return (r1, w1) - - -cdef class Cs(object): - - cdef cc.csh _csh - cdef object _cs - - def __cinit__(self, _cs): - cdef version = cc.cs_version(NULL, NULL) - if (version != (capstone.CS_API_MAJOR << 8) + capstone.CS_API_MINOR): - # our binding version is different from the core's API version - raise CsError(capstone.CS_ERR_VERSION) - - self._csh = _cs.csh.value - self._cs = _cs - - - # destructor to be called automatically when object is destroyed. - def __dealloc__(self): - if self._csh: - status = cc.cs_close(&self._csh) - if status != capstone.CS_ERR_OK: - raise CsError(status) - - - # Disassemble binary & return disassembled instructions in CsInsn objects - def disasm(self, code, addr, count=0): - cdef cc.cs_insn *allinsn - - cdef res = cc.cs_disasm(self._csh, code, len(code), addr, count, &allinsn) - detail = self._cs.detail - arch = self._cs.arch - - try: - for i from 0 <= i < res: - if detail: - dummy = CsInsn(CsDetail(arch, allinsn[i].detail)) - else: - dummy = CsInsn(None) - - dummy._raw = allinsn[i] - dummy._csh = self._csh - yield dummy - finally: - cc.cs_free(allinsn, res) - - - # Light function to disassemble binary. This is about 20% faster than disasm() because - # unlike disasm(), disasm_lite() only return tuples of (address, size, mnemonic, op_str), - # rather than CsInsn objects. - def disasm_lite(self, code, addr, count=0): - # TODO: dont need detail, so we might turn off detail, then turn on again when done - cdef cc.cs_insn *allinsn - - if _diet: - # Diet engine cannot provide @mnemonic & @op_str - raise CsError(capstone.CS_ERR_DIET) - - cdef res = cc.cs_disasm(self._csh, code, len(code), addr, count, &allinsn) - - try: - for i from 0 <= i < res: - insn = allinsn[i] - yield (insn.address, insn.size, insn.mnemonic, insn.op_str) - finally: - cc.cs_free(allinsn, res) - - -# print out debugging info -def debug(): - if cc.cs_support(capstone.CS_SUPPORT_DIET): - diet = "diet" - else: - diet = "standard" - - archs = { "arm": capstone.CS_ARCH_ARM, "arm64": capstone.CS_ARCH_ARM64, \ - "m68k": capstone.CS_ARCH_M68K, "mips": capstone.CS_ARCH_MIPS, \ - "ppc": capstone.CS_ARCH_PPC, "sparc": capstone.CS_ARCH_SPARC, \ - "sysz": capstone.CS_ARCH_SYSZ, "xcore": capstone.CS_ARCH_XCORE, \ - "tms320c64x": capstone.CS_ARCH_TMS320C64X, "m680x": capstone.CS_ARCH_M680X, \ - "evm": capstone.CS_ARCH_EVM, "mos65xx": capstone.CS_ARCH_MOS65XX, \ - "wasm": capstone.CS_ARCH_WASM, \ - "bpf": capstone.CS_ARCH_BPF, "riscv": capstone.CS_ARCH_RISCV, \ - "sh": capstone.CS_ARCH_SH, "tricore": capstone.CS_ARCH_TRICORE } - - all_archs = "" - keys = list(archs.keys()) - keys.sort() - for k in keys: - if cc.cs_support(archs[k]): - all_archs += "-%s" %k - - if cc.cs_support(capstone.CS_ARCH_X86): - all_archs += "-x86" - if cc.cs_support(capstone.CS_SUPPORT_X86_REDUCE): - all_archs += "_reduce" - - (major, minor, _combined) = capstone.cs_version() - - return "Cython-%s%s-c%u.%u-b%u.%u" %(diet, all_archs, major, minor, capstone.CS_API_MAJOR, capstone.CS_API_MINOR) diff --git a/bindings/python/setup.py b/bindings/python/setup.py index f7f6e7d794..f81632e280 100755 --- a/bindings/python/setup.py +++ b/bindings/python/setup.py @@ -6,16 +6,15 @@ import sys import platform -from distutils import log +import logging from setuptools import setup -from distutils.util import get_platform -from distutils.command.build import build -from distutils.command.sdist import sdist +from sysconfig import get_platform +from setuptools.command.build import build +from setuptools.command.sdist import sdist from setuptools.command.bdist_egg import bdist_egg -PYTHON2 = sys.version_info[0] == 2 -if PYTHON2: - import io +logger = logging.getLogger(__name__) +logging.basicConfig(level=logging.INFO) SYSTEM = sys.platform @@ -106,7 +105,7 @@ def copy_sources(): for filename in src: outpath = os.path.join(SRC_DIR, os.path.basename(filename)) - log.info("%s -> %s" % (filename, outpath)) + logger.info("%s -> %s" % (filename, outpath)) shutil.copy(filename, outpath) def build_libraries(): @@ -127,6 +126,7 @@ def build_libraries(): # if prebuilt libraries are available, use those and cancel build if os.path.exists(os.path.join(ROOT_DIR, 'prebuilt', LIBRARY_FILE)) and \ (not STATIC_LIBRARY_FILE or os.path.exists(os.path.join(ROOT_DIR, 'prebuilt', STATIC_LIBRARY_FILE))): + logger.info('Using prebuilt libraries') shutil.copy(os.path.join(ROOT_DIR, 'prebuilt', LIBRARY_FILE), LIBS_DIR) if STATIC_LIBRARY_FILE is not None: shutil.copy(os.path.join(ROOT_DIR, 'prebuilt', STATIC_LIBRARY_FILE), LIBS_DIR) @@ -165,9 +165,9 @@ def run(self): class custom_build(build): def run(self): if 'LIBCAPSTONE_PATH' in os.environ: - log.info('Skipping building C extensions since LIBCAPSTONE_PATH is set') + logger.info('Skipping building C extensions since LIBCAPSTONE_PATH is set') else: - log.info('Building C extensions') + logger.info('Building C extensions') build_libraries() return build.run(self) @@ -189,7 +189,7 @@ def dummy_src(): from setuptools.command.develop import develop class custom_develop(develop): def run(self): - log.info("Building C extensions") + logger.info("Building C extensions") build_libraries() return develop.run(self) @@ -220,19 +220,20 @@ def run(self): author_email='aquynh@gmail.com', description='Capstone disassembly engine', url='https://www.capstone-engine.org', - long_description=io.open('README.txt', encoding="utf8").read() if PYTHON2 else open('README.txt', encoding="utf8").read(), + long_description=open('README.txt', encoding="utf8").read(), long_description_content_type='text/markdown', - python_requires='>=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*', + python_requires='>=3.8', classifiers=[ 'License :: OSI Approved :: BSD License', - 'Programming Language :: Python :: 2', - 'Programming Language :: Python :: 2.7', 'Programming Language :: Python :: 3', ], cmdclass=cmdclass, - zip_safe=True, + zip_safe=False, include_package_data=True, package_data={ "capstone": ["lib/*", "include/capstone/*"], - } + }, + install_requires=[ + "importlib_resources;python_version<'3.9'", + ], ) diff --git a/bindings/python/setup_cython.py b/bindings/python/setup_cython.py deleted file mode 100644 index 70fcc856c9..0000000000 --- a/bindings/python/setup_cython.py +++ /dev/null @@ -1,151 +0,0 @@ -import os -import sys -import shutil - -from distutils import log -from distutils.core import setup -from distutils.extension import Extension -from distutils.command.build import build -from Cython.Distutils import build_ext - -SYSTEM = sys.platform -VERSION = '5.0.0' -VERSION_PARTS = VERSION.split(".") - -# adapted from commit e504b81 of Nguyen Tan Cong -# Reference: https://docs.python.org/2/library/platform.html#cross-platform -IS_64BITS = sys.maxsize > 2**32 - -# are we building from the repository or from a source distribution? -ROOT_DIR = os.path.dirname(os.path.realpath(__file__)) -LIBS_DIR = os.path.join(ROOT_DIR, 'pyx', 'lib') -HEADERS_DIR = os.path.join(ROOT_DIR, 'pyx', 'include') -SRC_DIR = os.path.join(ROOT_DIR, 'src') -BUILD_DIR = SRC_DIR if os.path.exists(SRC_DIR) else os.path.join(ROOT_DIR, '../..') -PYPACKAGE_DIR = os.path.join(ROOT_DIR, 'capstone') -CYPACKAGE_DIR = os.path.join(ROOT_DIR, 'pyx') - -if SYSTEM == 'darwin': - VERSIONED_LIBRARY_FILE = "libcapstone.{}.dylib".format(VERSION_PARTS[0]) - LIBRARY_FILE = "libcapstone.dylib" - STATIC_LIBRARY_FILE = 'libcapstone.a' -elif SYSTEM in ('win32', 'cygwin'): - VERSIONED_LIBRARY_FILE = "capstone.dll" - LIBRARY_FILE = "capstone.dll" - STATIC_LIBRARY_FILE = None -else: - VERSIONED_LIBRARY_FILE = "libcapstone.so.{}".format(VERSION_PARTS[0]) - LIBRARY_FILE = "libcapstone.so" - STATIC_LIBRARY_FILE = 'libcapstone.a' - -compile_args = ['-O3', '-fomit-frame-pointer', '-I' + HEADERS_DIR] -link_args = ['-L' + LIBS_DIR] - -ext_module_names = ['arm', 'arm_const', 'arm64', 'arm64_const', 'm68k', 'm68k_const', 'm680x', 'm680x_const', 'mips', 'mips_const', 'ppc', 'ppc_const', 'x86', 'x86_const', 'sparc', 'sparc_const', 'systemz', 'sysz_const', 'xcore', 'xcore_const', 'tms320c64x', 'tms320c64x_const', 'evm', 'evm_const', 'mos65xx', 'mos65xx_const', 'wasm', 'wasm_const', 'bpf', 'bpf_const', 'riscv', 'riscv_const', 'sh', 'sh_const', 'tricore', 'tricore_const' ] - -ext_modules = [Extension("capstone.ccapstone", - ["pyx/ccapstone.pyx"], - libraries=["capstone"], - extra_compile_args=compile_args, - extra_link_args=link_args)] -ext_modules += [Extension("capstone.%s" % name, - ["pyx/%s.pyx" % name], - extra_compile_args=compile_args, - extra_link_args=link_args) - for name in ext_module_names] - -for e in ext_modules: - e.cython_directives = {"language_level": str(sys.version_info[0])} - - -def clean_bins(): - shutil.rmtree(LIBS_DIR, ignore_errors=True) - shutil.rmtree(HEADERS_DIR, ignore_errors=True) - -def copy_pysources(): - for fname in os.listdir(PYPACKAGE_DIR): - if not fname.endswith('.py'): - continue - - if fname == '__init__.py': - shutil.copy(os.path.join(PYPACKAGE_DIR, fname), os.path.join(CYPACKAGE_DIR, fname)) - else: - shutil.copy(os.path.join(PYPACKAGE_DIR, fname), os.path.join(CYPACKAGE_DIR, fname + 'x')) - -def build_libraries(): - """ - Prepare the capstone directory for a binary distribution or installation. - Builds shared libraries and copies header files. - - Will use a src/ dir if one exists in the current directory, otherwise assumes it's in the repo - """ - cwd = os.getcwd() - clean_bins() - os.mkdir(HEADERS_DIR) - os.mkdir(LIBS_DIR) - - # copy public headers - shutil.copytree(os.path.join(BUILD_DIR, 'include', 'capstone'), os.path.join(HEADERS_DIR, 'capstone')) - - os.chdir(BUILD_DIR) - - # platform description refers at https://docs.python.org/2/library/sys.html#sys.platform - if SYSTEM == "win32": - # Windows build: this process requires few things: - # - CMake + MSVC installed - # - Run this command in an environment setup for MSVC - if not os.path.exists("build"): os.mkdir("build") - os.chdir("build") - # Only build capstone.dll - os.system('cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_BUILD_SHARED=ON -DCAPSTONE_BUILD_TESTS=OFF -DCAPSTONE_BUILD_CSTOOL=OFF -G "NMake Makefiles" ..') - os.system("cmake --build .") - else: # Unix incl. cygwin - os.system("CAPSTONE_BUILD_CORE_ONLY=yes bash ./make.sh") - - shutil.copy(VERSIONED_LIBRARY_FILE, os.path.join(LIBS_DIR, LIBRARY_FILE)) - if STATIC_LIBRARY_FILE: shutil.copy(STATIC_LIBRARY_FILE, LIBS_DIR) - os.chdir(cwd) - - -class custom_build(build): - def run(self): - log.info('Copying python sources') - copy_pysources() - log.info('Building C extensions') - build_libraries() - return build.run(self) - -# clean package directory first -#import os.path, shutil, sys -#for f in sys.path: -# if f.endswith('packages'): -# pkgdir = os.path.join(f, 'capstone') -# #print(pkgdir) -# try: -# shutil.rmtree(pkgdir) -# except: -# pass - -setup( - provides = ['capstone'], - package_dir = {'capstone' : 'pyx'}, - packages = ['capstone'], - name = 'capstone', - version = VERSION, - cmdclass = {'build_ext': build_ext, 'build': custom_build}, - ext_modules = ext_modules, - author = 'Nguyen Anh Quynh', - author_email = 'aquynh@gmail.com', - description = 'Capstone disassembly engine', - url = 'http://www.capstone-engine.org', - classifiers = [ - 'License :: OSI Approved :: BSD License', - 'Programming Language :: Python :: 2', - 'Programming Language :: Python :: 2.7', - 'Programming Language :: Python :: 3', - ], - include_package_data=True, - package_data={ - "capstone": ["lib/*", "include/capstone/*"], - } -) diff --git a/bindings/python/test_all.py b/bindings/python/tests/test_all.py similarity index 100% rename from bindings/python/test_all.py rename to bindings/python/tests/test_all.py diff --git a/bindings/python/test_arm.py b/bindings/python/tests/test_arm.py similarity index 100% rename from bindings/python/test_arm.py rename to bindings/python/tests/test_arm.py diff --git a/bindings/python/test_arm64.py b/bindings/python/tests/test_arm64.py similarity index 100% rename from bindings/python/test_arm64.py rename to bindings/python/tests/test_arm64.py diff --git a/bindings/python/test_basic.py b/bindings/python/tests/test_basic.py similarity index 100% rename from bindings/python/test_basic.py rename to bindings/python/tests/test_basic.py diff --git a/bindings/python/test_bpf.py b/bindings/python/tests/test_bpf.py similarity index 100% rename from bindings/python/test_bpf.py rename to bindings/python/tests/test_bpf.py diff --git a/bindings/python/test_customized_mnem.py b/bindings/python/tests/test_customized_mnem.py similarity index 100% rename from bindings/python/test_customized_mnem.py rename to bindings/python/tests/test_customized_mnem.py diff --git a/bindings/python/test_detail.py b/bindings/python/tests/test_detail.py similarity index 100% rename from bindings/python/test_detail.py rename to bindings/python/tests/test_detail.py diff --git a/bindings/python/test_evm.py b/bindings/python/tests/test_evm.py similarity index 100% rename from bindings/python/test_evm.py rename to bindings/python/tests/test_evm.py diff --git a/bindings/python/test_lite.py b/bindings/python/tests/test_lite.py similarity index 100% rename from bindings/python/test_lite.py rename to bindings/python/tests/test_lite.py diff --git a/bindings/python/test_m680x.py b/bindings/python/tests/test_m680x.py similarity index 100% rename from bindings/python/test_m680x.py rename to bindings/python/tests/test_m680x.py diff --git a/bindings/python/test_m68k.py b/bindings/python/tests/test_m68k.py similarity index 100% rename from bindings/python/test_m68k.py rename to bindings/python/tests/test_m68k.py diff --git a/bindings/python/test_mips.py b/bindings/python/tests/test_mips.py similarity index 100% rename from bindings/python/test_mips.py rename to bindings/python/tests/test_mips.py diff --git a/bindings/python/test_mos65xx.py b/bindings/python/tests/test_mos65xx.py similarity index 100% rename from bindings/python/test_mos65xx.py rename to bindings/python/tests/test_mos65xx.py diff --git a/bindings/python/test_ppc.py b/bindings/python/tests/test_ppc.py similarity index 100% rename from bindings/python/test_ppc.py rename to bindings/python/tests/test_ppc.py diff --git a/bindings/python/test_riscv.py b/bindings/python/tests/test_riscv.py similarity index 100% rename from bindings/python/test_riscv.py rename to bindings/python/tests/test_riscv.py diff --git a/bindings/python/test_sh.py b/bindings/python/tests/test_sh.py similarity index 100% rename from bindings/python/test_sh.py rename to bindings/python/tests/test_sh.py diff --git a/bindings/python/test_skipdata.py b/bindings/python/tests/test_skipdata.py similarity index 100% rename from bindings/python/test_skipdata.py rename to bindings/python/tests/test_skipdata.py diff --git a/bindings/python/test_sparc.py b/bindings/python/tests/test_sparc.py similarity index 100% rename from bindings/python/test_sparc.py rename to bindings/python/tests/test_sparc.py diff --git a/bindings/python/test_systemz.py b/bindings/python/tests/test_systemz.py similarity index 100% rename from bindings/python/test_systemz.py rename to bindings/python/tests/test_systemz.py diff --git a/bindings/python/test_tms320c64x.py b/bindings/python/tests/test_tms320c64x.py similarity index 100% rename from bindings/python/test_tms320c64x.py rename to bindings/python/tests/test_tms320c64x.py diff --git a/bindings/python/test_tricore.py b/bindings/python/tests/test_tricore.py similarity index 100% rename from bindings/python/test_tricore.py rename to bindings/python/tests/test_tricore.py diff --git a/bindings/python/test_wasm.py b/bindings/python/tests/test_wasm.py similarity index 100% rename from bindings/python/test_wasm.py rename to bindings/python/tests/test_wasm.py diff --git a/bindings/python/test_x86.py b/bindings/python/tests/test_x86.py similarity index 100% rename from bindings/python/test_x86.py rename to bindings/python/tests/test_x86.py diff --git a/bindings/python/test_xcore.py b/bindings/python/tests/test_xcore.py similarity index 100% rename from bindings/python/test_xcore.py rename to bindings/python/tests/test_xcore.py diff --git a/bindings/python/xprint.py b/bindings/python/tests/xprint.py similarity index 100% rename from bindings/python/xprint.py rename to bindings/python/tests/xprint.py